avatar of 发明者量化-小小梦 发明者量化-小小梦
fokus pada Pesan pribadi
4
fokus pada
1271
Pengikut

Strategi arbitrase antar periode mata uang kripto berdasarkan Bollinger Bands

Dibuat di: 2020-02-22 18:54:23, diperbarui pada: 2023-10-10 21:11:20
comments   0
hits   4202

Strategi arbitrase antar periode mata uang kripto berdasarkan Bollinger Bands

I. Ringkasan

Dalam bukunya The Alchemy of Finance yang ditulis pada tahun 1987, Soros pernah mengemukakan sebuah proposisi penting: Saya percaya harga pasar selalu salah dalam arti bahwa harga tersebut menyajikan pandangan yang bias terhadap masa depan. Hipotesis pasar yang efisien hanyalah sebuah teori. Faktanya, pelaku pasar tidak selalu rasional, dan pada setiap titik waktu, mustahil bagi para pelaku untuk memperoleh dan menafsirkan semua informasi secara objektif. Selain itu, meskipun informasinya sama, tanggapan setiap orang berbeda-beda. Berbeda-beda. Dengan kata lain, harga itu sendiri sudah mencakup ekspektasi keliru dari para pelaku pasar, sehingga harga pasar pada hakikatnya selalu salah. Ini mungkin menjadi sumber keuntungan bagi para pelaku arbitrase.

2. Prinsip Strategi

Berdasarkan prinsip-prinsip di atas, kita tahu bahwa dalam pasar berjangka yang tidak efisien, dampak pasar terhadap kontrak pengiriman di berbagai periode tidak selalu sinkron, dan harganya tidak sepenuhnya efektif. Kemudian, berdasarkan harga kontrak pengiriman subjek perdagangan yang sama dalam periode yang berbeda, jika ada perbedaan harga yang besar antara kedua harga tersebut, Anda dapat membeli dan menjual kontrak berjangka dari periode yang berbeda pada saat yang sama untuk melakukan arbitrase lintas periode. Seperti komoditas berjangka, mata uang digital juga memiliki kombinasi kontrak arbitrase lintas periode yang terkait dengannya. Misalnya, di bursa OkEX, ada: ETC Mingguan, ETC Dua Mingguan, dan ETC Triwulanan.

Misalnya, anggaplah perbedaan harga antara ETC mingguan dan ETC triwulanan tetap sekitar 5 untuk waktu yang lama. Jika selisihnya mencapai 7 pada suatu hari, kami perkirakan selisihnya akan kembali ke 5 pada suatu titik di masa mendatang. Selanjutnya Anda dapat menjual ETC setiap minggu dan membeli ETC setiap tiga bulan untuk memperpendek spread. sebaliknya. Meskipun perbedaan harga ini ada, arbitrase manual sering kali melibatkan banyak ketidakpastian karena operasi manual yang memakan waktu, akurasi yang buruk, dan dampak perubahan harga. Pesona arbitrase kuantitatif terletak pada penangkapan peluang arbitrase melalui model kuantitatif dan perumusan strategi perdagangan arbitrase, serta penempatan perintah perdagangan secara otomatis ke bursa melalui algoritma terprogram, sehingga dapat menangkap peluang dengan cepat dan akurat serta memperoleh laba secara efisien dan stabil.

3. Logika Strategi

Artikel ini akan mengajarkan Anda cara menggunakan Platform Perdagangan Kuantitatif Inventor dan kontrak berjangka ETC di bursa OkEX dalam perdagangan mata uang digital, menggunakan strategi arbitrase sederhana untuk menunjukkan cara menangkap peluang arbitrase instan dan memanfaatkan setiap peluang untuk melihat Dapatkan keuntungan saat melakukan lindung nilai kemungkinan risiko.

Membuat Strategi Arbitrase Lintas Periode Mata Uang Kripto Kesulitan: Normal

Lingkungan Strategis

  • Subjek transaksi: Ethereum Classic (ETC)
  • Data perbedaan harga: ETC mingguan - ETC triwulanan (pengujian kointegrasi dihilangkan)
  • Siklus perdagangan: 5 menit
  • Pencocokan posisi: 1:1
  • Jenis transaksi: Produk yang sama lintas periode

Logika Strategi

  • Kondisi pembukaan spread panjang: Jika akun berjalan tidak memiliki posisi dan spread lebih kecil dari jalur boll bawah, ambil posisi panjang pada spread. Yaitu: beli ETC selama seminggu dan jual ETC selama seperempat tahun.
  • Ketentuan untuk membuka posisi short spread: Jika rekening giro tidak memiliki posisi dan spread lebih besar dari boll upper track, short spread. Yaitu: jual ETC selama seminggu dan beli ETC selama seperempat tahun.
  • Ketentuan untuk menutup spread panjang: Jika akun berjalan memiliki posisi panjang mingguan di ETC dan posisi pendek triwulanan di ETC, dan spread lebih besar dari jalur tengah boll, spread panjang akan ditutup. Yaitu: jual ETC selama seminggu dan beli ETC selama seperempat tahun.
  • Ketentuan untuk menutup spread short: Jika akun berjalan memiliki posisi short untuk ETC minggu ini dan posisi long untuk ETC pada kuartal ini, dan spread kurang dari jalur tengah boll, spread short akan ditutup. Yaitu: beli ETC selama seminggu dan jual ETC selama seperempat tahun.

4. Tulis kerangka kebijakan

Di atas adalah deskripsi sederhana tentang logika strategi arbitrase lintas periode mata uang digital. Jadi, bagaimana Anda menerapkan ide-ide Anda dalam program tersebut? Kami mencoba membangun kerangka kerja pada Platform Perdagangan Kuantitatif Inventor terlebih dahulu.

function Data() {}  // 基础数据函数
Data.prototype.mp = function () {}  // 持仓函数
Data.prototype.boll = function () {}  // 指标函数
Data.prototype.trade = function () {}  // 下单函数
Data.prototype.cancelOrders = function () {}  // 撤单函数
Data.prototype.isEven = function () {}  // 处理单只合约函数
Data.prototype.drawingChart = function () {}  // 画图函数

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB);  // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks;  // 账户余额
    var boll = data.boll(dataLength, timeCycle);  // 计算boll技术指标
    data.trade();  // 计算交易条件下单
    data.cancelOrders();  // 撤单
    data.drawingChart(boll);  // 画图
    data.isEven();  // 处理持有单个合约
}

//入口函数
function main() {
    while (true) {  // 进入轮询模式
        onTick();  // 执行onTick函数
        Sleep(500);  // 休眠0.5秒
    }
}

5. Tulislah sebuah Strategi

Dengan membandingkan ide-ide strategis dan proses perdagangan, Anda dapat dengan mudah membangun kerangka strategi. Seluruh strategi dapat disederhanakan menjadi tiga langkah:

  • Pemrosesan pra-transaksi.
  • Mendapatkan dan menghitung data.
  • Tempatkan pesanan dan tangani tindak lanjutnya.

Berikutnya, kita perlu mengisi kode detail yang diperlukan dalam kerangka strategi berdasarkan proses transaksi aktual dan detail transaksi.

Pemrosesan pra-transaksi Langkah 1: Di lingkungan global, deklarasikan variabel global yang diperlukan.

//声明一个配置图表的 chart 对象
var chart = { }

//调用 Chart 函数,初始化图表
var ObjChart = Chart ( chart )

//声明一个空数组,用来存储价差序列
var bars = [ ]

//声明一个记录历史数据时间戳变量
var oldTime = 0

Langkah 2: Konfigurasikan parameter eksternal strategi.

// 参数
var tradeTypeA = "this_week"; // 套利A合约
var tradeTypeB = "quarter"; // 套利B合约
var dataLength = 10; //指标周期长度
var timeCycle = 1; // K线周期
var name = "ETC"; // 币种
var unit = 1; // 下单量

Langkah 3: Tentukan fungsi pemrosesan data Fungsi data dasar: Data ( ) Buat konstruktor Data dan tentukan properti internalnya. Meliputi: data akun, data posisi, stempel waktu data K-line, harga bid/ask kontrak arbitrase A/B, dan spread arbitrase forward/reverse.

// 基础数据
function Data(tradeTypeA, tradeTypeB) { // 传入套利A合约和套利B合约
    this.accountData = _C(exchange.GetAccount); // 获取账户信息
    this.positionData = _C(exchange.GetPosition); // 获取持仓信息
    var recordsData = _C(exchange.GetRecords); //获取K线数据
    exchange.SetContractType(tradeTypeA); // 订阅套利A合约
    var depthDataA = _C(exchange.GetDepth); // 套利A合约深度数据
    exchange.SetContractType(tradeTypeB); // 订阅套利B合约
    var depthDataB = _C(exchange.GetDepth); // 套利B合约深度数据
    this.time = recordsData[recordsData.length - 1].Time; // 获取最新数据时间
    this.askA = depthDataA.Asks[0].Price; // 套利A合约卖一价
    this.bidA = depthDataA.Bids[0].Price; // 套利A合约买一价
    this.askB = depthDataB.Asks[0].Price; // 套利B合约卖一价
    this.bidB = depthDataB.Bids[0].Price; // 套利B合约买一价
    // 正套价差(合约A卖一价 - 合约B买一价)
    this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price;
    // 反套价差(合约A买一价 - 合约B卖一价)
    this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price;
}

Dapatkan fungsi posisi: mp ( ) Lintasi seluruh susunan posisi dan kembalikan jumlah posisi kontrak dan arah yang ditentukan. Jika tidak ada, kembalikan false

// 获取持仓
Data.prototype.mp = function (tradeType, type) {
    var positionData = this.positionData; // 获取持仓信息
    for (var i = 0; i < positionData.length; i++) {
        if (positionData[i].ContractType == tradeType) {
            if (positionData[i].Type == type) {
                if (positionData[i].Amount > 0) {
                    return positionData[i].Amount;
                }
            }
        }
    }
    return false;
}

Fungsi garis K dan indikator: boll ( ) Mensintesiskan sekuens K-line baru berdasarkan data sebaran arbitrase maju/terbalik. Dan mengembalikan data rel atas, rel tengah, dan rel bawah yang dihitung oleh indikator boll.

// 合成新K线数据和boll指标数据
Data.prototype.boll = function (num, timeCycle) {
    var self = {}; // 临时对象
    // 正套价差和反套价差中间值
    self.Close = (this.basb + this.sabb) / 2;
    if (this.timeA == this.timeB) {
        self.Time = this.time;
    } // 对比两个深度数据时间戳
    if (this.time - oldTime > timeCycle * 60000) {
        bars.push(self);
        oldTime = this.time;
    } // 根据指定时间周期,在K线数组里面传入价差数据对象
    if (bars.length > num * 2) {
        bars.shift(); // 控制K线数组长度
    } else {
        return;
    }
    var boll = TA.BOLL(bars, num, 2); // 调用talib库中的boll指标
    return {
        up: boll[0][boll[0].length - 1], // boll指标上轨
        middle: boll[1][boll[1].length - 1], // boll指标中轨
        down: boll[2][boll[2].length - 1] // boll指标下轨
    } // 返回一个处理好的boll指标数据
}

Fungsi pesanan: perdagangan ( ) Masukkan nama kontrak pesanan dan jenis pesanan, lalu tempatkan pesanan pada harga pertimbangan, dan kembalikan hasilnya setelah tempatkan pesanan. Karena perlu menempatkan dua pesanan ke arah berbeda pada saat yang sama, harga beli/jual dikonversi dalam fungsi sesuai dengan nama kontrak pesanan.

// 下单
Data.prototype.trade = function (tradeType, type) {
    exchange.SetContractType(tradeType); // 下单前先重新订阅合约
    var askPrice, bidPrice;
    if (tradeType == tradeTypeA) { // 如果是A合约下单
        askPrice = this.askA; // 设置askPrice
        bidPrice = this.bidA; // 设置bidPrice
    } else if (tradeType == tradeTypeB) { // 如果是B合约下单
        askPrice = this.askB; // 设置askPrice
        bidPrice = this.bidB; // 设置bidPrice
    }
    switch (type) { // 匹配下单模式
        case "buy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        case "sell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closebuy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closesell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        default:
            return false;
    }
}

Fungsi pembatalan pesanan: cancelOrders() Dapatkan serangkaian pesanan yang tidak terpenuhi dan batalkan satu per satu. Dan jika ada pesanan yang tidak terpenuhi, maka akan mengembalikan nilai false, dan jika tidak ada pesanan yang tidak terpenuhi, maka akan mengembalikan nilai true.

// 取消订单
Data.prototype.cancelOrders = function () {
    Sleep(500); // 撤单前先延时,因为有些交易所你懂的
    var orders = _C(exchange.GetOrders); // 获取未成交订单数组
    if (orders.length > 0) { // 如果有未成交的订单
        for (var i = 0; i < orders.length; i++) { //遍历未成交订单数组
            exchange.CancelOrder(orders[i].Id); //逐个取消未成交的订单
            Sleep(500); //延时0.5秒
        }
        return false; // 如果取消了未成交的单子就返回false
    }
    return true; //如果没有未成交的订单就返回true
}

Menangani penyimpanan satu kontrak: isEven() Saat berhadapan dengan situasi single-leg dalam perdagangan arbitrase, kami cukup menutup semua posisi untuk menanganinya. Tentu saja Anda juga dapat mengubahnya ke metode pemesanan lanjutan.

// 处理持有单个合约
Data.prototype.isEven = function () {
    var positionData = this.positionData; // 获取持仓信息
    var type = null; // 转换持仓方向
    // 如果持仓数组长度余2不等于0或者持仓数组长度不等于2
    if (positionData.length % 2 != 0 || positionData.length != 2) {
        for (var i = 0; i < positionData.length; i++) { // 遍历持仓数组
            if (positionData[i].Type == 0) { // 如果是多单
                type = 10; // 设置下单参数
            } else if (positionData[i].Type == 1) { // 如果是空单
                type = -10; // 设置下单参数
            }
            // 平掉所有仓位
            this.trade(positionData[i].ContractType, type, positionData[i].Amount);
        }
    }
}

Fungsi menggambar: drawingChart ( ) Panggil metode ObjChart.add() untuk menggambar data pasar dan data indikator yang diperlukan dalam grafik: jalur atas, jalur tengah, jalur bawah, dan spread positif/negatif.

// 画图
Data.prototype.drawingChart = function (boll) {
    var nowTime = new Date().getTime();
    ObjChart.add([0, [nowTime, boll.up]]);
    ObjChart.add([1, [nowTime, boll.middle]]);
    ObjChart.add([2, [nowTime, boll.down]]);
    ObjChart.add([3, [nowTime, this.basb]]);
    ObjChart.add([4, [nowTime, this.sabb]]);
    ObjChart.update(chart);
}

Langkah 4: Pada fungsi entri main(), jalankan kode praproses pratransaksi. Kode ini hanya dijalankan sekali setelah program dimulai. termasuk:

  • Filter pesan yang kurang penting di konsol SetErrorFilter ()
  • Tetapkan mata uang digital yang akan diperdagangkan exchange.IO ( )
  • Hapus grafik yang digambar sebelumnya sebelum program dimulai ObjChart.reset ( )
  • Hapus informasi bilah status sebelumnya sebelum program dimulai LogProfitReset ( )
//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
}

Setelah mendefinisikan pra-pemrosesan perdagangan di atas, kita akan beralih ke langkah berikutnya, memasuki mode pemungutan suara, dan berulang kali mengeksekusi fungsi onTick(). Dan atur waktu tidur saat Sleep() melakukan polling, karena beberapa API pertukaran mata uang kripto memiliki batas akses bawaan dalam jangka waktu tertentu.

//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
    while (true) { // 进入轮询模式
        onTick(); // 执行onTick函数
        Sleep(500); // 休眠0.5秒
    }
}

Dapatkan dan hitung data Langkah 1: Dapatkan objek data dasar, saldo akun, dan data indikator boll untuk digunakan dalam logika perdagangan.

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
}

Tempatkan pesanan dan tindak lanjuti Langkah 1: Jalankan operasi beli dan jual sesuai dengan logika strategi di atas. Pertama, menentukan apakah kondisi harga dan indikator terpenuhi, kemudian menentukan apakah kondisi posisi terpenuhi, dan akhirnya mengeksekusi fungsi order perdagangan ( )

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
}

Langkah 2: Setelah pesanan dilakukan, situasi abnormal seperti pesanan yang tidak terpenuhi dan memegang satu kontrak perlu ditangani. dan menggambar grafik.

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
    data.cancelOrders(); // 撤单
    data.drawingChart(boll); // 画图
    data.isEven(); // 处理持有单个合约
}

6. Strategi Lengkap

Di atas, kami telah membuat strategi arbitrase antar-periode mata uang digital sederhana hanya dalam 200 baris. Kode lengkapnya adalah sebagai berikut:

// 全局变量
// 声明一个配置图表的 chart 对象
var chart = {
    __isStock: true,
    tooltip: {
        xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
    },
    title: {
        text: '交易盈亏曲线图(详细)'
    },
    rangeSelector: {
        buttons: [{
            type: 'hour',
            count: 1,
            text: '1h'
        }, {
            type: 'hour',
            count: 2,
            text: '3h'
        }, {
            type: 'hour',
            count: 8,
            text: '8h'
        }, {
            type: 'all',
            text: 'All'
        }],
        selected: 0,
        inputEnabled: false
    },
    xAxis: {
        type: 'datetime'
    },
    yAxis: {
        title: {
            text: '价差'
        },
        opposite: false,
    },
    series: [{
        name: "上轨",
        id: "线1,up",
        data: []
    }, {
        name: "中轨",
        id: "线2,middle",
        data: []
    }, {
        name: "下轨",
        id: "线3,down",
        data: []
    }, {
        name: "basb",
        id: "线4,basb",
        data: []
    }, {
        name: "sabb",
        id: "线5,sabb",
        data: []
    }]
};
var ObjChart = Chart(chart); // 画图对象
var bars = []; // 存储价差序列
var oldTime = 0; // 记录历史数据时间戳

// 参数
var tradeTypeA = "this_week"; // 套利A合约
var tradeTypeB = "quarter"; // 套利B合约
var dataLength = 10; //指标周期长度
var timeCycle = 1; // K线周期
var name = "ETC"; // 币种
var unit = 1; // 下单量

// 基础数据
function Data(tradeTypeA, tradeTypeB) { // 传入套利A合约和套利B合约
    this.accountData = _C(exchange.GetAccount); // 获取账户信息
    this.positionData = _C(exchange.GetPosition); // 获取持仓信息
    var recordsData = _C(exchange.GetRecords); //获取K线数据
    exchange.SetContractType(tradeTypeA); // 订阅套利A合约
    var depthDataA = _C(exchange.GetDepth); // 套利A合约深度数据
    exchange.SetContractType(tradeTypeB); // 订阅套利B合约
    var depthDataB = _C(exchange.GetDepth); // 套利B合约深度数据
    this.time = recordsData[recordsData.length - 1].Time; // 获取最新数据时间
    this.askA = depthDataA.Asks[0].Price; // 套利A合约卖一价
    this.bidA = depthDataA.Bids[0].Price; // 套利A合约买一价
    this.askB = depthDataB.Asks[0].Price; // 套利B合约卖一价
    this.bidB = depthDataB.Bids[0].Price; // 套利B合约买一价
    // 正套价差(合约A卖一价 - 合约B买一价)
    this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price;
    // 反套价差(合约A买一价 - 合约B卖一价)
    this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price;
}

// 获取持仓
Data.prototype.mp = function (tradeType, type) {
    var positionData = this.positionData; // 获取持仓信息
    for (var i = 0; i < positionData.length; i++) {
        if (positionData[i].ContractType == tradeType) {
            if (positionData[i].Type == type) {
                if (positionData[i].Amount > 0) {
                    return positionData[i].Amount;
                }
            }
        }
    }
    return false;
}

// 合成新K线数据和boll指标数据
Data.prototype.boll = function (num, timeCycle) {
    var self = {}; // 临时对象
    // 正套价差和反套价差中间值
    self.Close = (this.basb + this.sabb) / 2;
    if (this.timeA == this.timeB) {
        self.Time = this.time;
    } // 对比两个深度数据时间戳
    if (this.time - oldTime > timeCycle * 60000) {
        bars.push(self);
        oldTime = this.time;
    } // 根据指定时间周期,在K线数组里面传入价差数据对象
    if (bars.length > num * 2) {
        bars.shift(); // 控制K线数组长度
    } else {
        return;
    }
    var boll = TA.BOLL(bars, num, 2); // 调用talib库中的boll指标
    return {
        up: boll[0][boll[0].length - 1], // boll指标上轨
        middle: boll[1][boll[1].length - 1], // boll指标中轨
        down: boll[2][boll[2].length - 1] // boll指标下轨
    } // 返回一个处理好的boll指标数据
}

// 下单
Data.prototype.trade = function (tradeType, type) {
    exchange.SetContractType(tradeType); // 下单前先重新订阅合约
    var askPrice, bidPrice;
    if (tradeType == tradeTypeA) { // 如果是A合约下单
        askPrice = this.askA; // 设置askPrice
        bidPrice = this.bidA; // 设置bidPrice
    } else if (tradeType == tradeTypeB) { // 如果是B合约下单
        askPrice = this.askB; // 设置askPrice
        bidPrice = this.bidB; // 设置bidPrice
    }
    switch (type) { // 匹配下单模式
        case "buy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        case "sell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closebuy":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Sell(bidPrice, unit);
        case "closesell":
            exchange.SetDirection(type); // 设置下单模式
            return exchange.Buy(askPrice, unit);
        default:
            return false;
    }
}

// 取消订单
Data.prototype.cancelOrders = function () {
    Sleep(500); // 撤单前先延时,因为有些交易所你懂的
    var orders = _C(exchange.GetOrders); // 获取未成交订单数组
    if (orders.length > 0) { // 如果有未成交的订单
        for (var i = 0; i < orders.length; i++) { //遍历未成交订单数组
            exchange.CancelOrder(orders[i].Id); //逐个取消未成交的订单
            Sleep(500); //延时0.5秒
        }
        return false; // 如果取消了未成交的单子就返回false
    }
    return true; //如果没有未成交的订单就返回true
}

// 处理持有单个合约
Data.prototype.isEven = function () {
    var positionData = this.positionData; // 获取持仓信息
    var type = null; // 转换持仓方向
    // 如果持仓数组长度余2不等于0或者持仓数组长度不等于2
    if (positionData.length % 2 != 0 || positionData.length != 2) {
        for (var i = 0; i < positionData.length; i++) { // 遍历持仓数组
            if (positionData[i].Type == 0) { // 如果是多单
                type = 10; // 设置下单参数
            } else if (positionData[i].Type == 1) { // 如果是空单
                type = -10; // 设置下单参数
            }
            // 平掉所有仓位
            this.trade(positionData[i].ContractType, type, positionData[i].Amount);
        }
    }
}

// 画图
Data.prototype.drawingChart = function (boll) {
    var nowTime = new Date().getTime();
    ObjChart.add([0, [nowTime, boll.up]]);
    ObjChart.add([1, [nowTime, boll.middle]]);
    ObjChart.add([2, [nowTime, boll.down]]);
    ObjChart.add([3, [nowTime, this.basb]]);
    ObjChart.add([4, [nowTime, this.sabb]]);
    ObjChart.update(chart);
}

// 交易条件
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象
    var accountStocks = data.accountData.Stocks; // 账户余额
    var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据
    if (!boll) return; // 如果没有boll数据就返回
    // 价差说明
    // basb = (合约A卖一价 - 合约B买一价)
    // sabb = (合约A买一价 - 合约B卖一价)
    if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨
        if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
            data.trade(tradeTypeA, "closebuy"); // 合约A平多
        }
        if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
            data.trade(tradeTypeB, "closesell"); // 合约B平空
        }
    } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨
        if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
            data.trade(tradeTypeA, "closesell"); // 合约A平空
        }
        if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
            data.trade(tradeTypeB, "closebuy"); // 合约B平多
        }
    }
    if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额
        if (data.basb < boll.down) { // 如果basb价差低于下轨
            if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单
                data.trade(tradeTypeA, "buy"); // 合约A开多
            }
            if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单
                data.trade(tradeTypeB, "sell"); // 合约B开空
            }
        } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨
            if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单
                data.trade(tradeTypeA, "sell"); // 合约A开空
            }
            if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单
                data.trade(tradeTypeB, "buy"); // 合约B开多
            }
        }
    }
    data.cancelOrders(); // 撤单
    data.drawingChart(boll); // 画图
    data.isEven(); // 处理持有单个合约
}

//入口函数
function main() {
    // 过滤控制台中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //设置要交易的数字货币币种
    ObjChart.reset(); //程序启动前清空之前绘制的图表
    LogProfitReset(); //程序启动前清空之前的状态栏信息
    while (true) { // 进入轮询模式
        onTick(); // 执行onTick函数
        Sleep(500); // 休眠0.5秒
    }
}

Alamat Kebijakan: https://www.fmz.com/strategy/104964

VII. Kesimpulan

Strategi ini hanyalah titik awal. Perdagangan sebenarnya tidak sesederhana itu, tetapi Anda dapat menggunakan contoh-contoh untuk memaksimalkan imajinasi Anda. Saya perlu mengingatkan semua orang bahwa, berdasarkan pengalaman saya yang terbatas, mengingat keadaan pasar mata uang digital saat ini, strategi arbitrase murni periode ke periode pada dasarnya tidak layak dijalankan, baik itu arbitrase segitiga bebas risiko atau arbitrase lintas pasar. .

Alasannya adalah karena di pasar berjangka bursa mata uang digital mana pun, margin bukanlah mata uang yang sah. Saat ini, hampir semua mata uang digital telah jatuh sekitar 70% sejak awal tahun ini. Dengan kata lain, strateginya selalu menghasilkan uang, tetapi harga mata uangnya turun. Jika melihat ke sekeliling, pasar mata uang digital tampaknya telah terpisah dari blockchain. Sama seperti bunga tulip pada masa itu, harga selalu berasal dari ekspektasi dan keyakinan masyarakat, dan keyakinan berasal dari harga…