索羅斯在1987年撰寫的《金融鍊金術》 一書中,曾經提出過一個重要的命題:I believe the market prices are always wrong in the sense that they present a biased view of the future. 算法
市場有效假說只是理論上的假設,實際上市場參與者並不老是理性的,而且在每個時間點上,參與者不可能徹底獲取和客觀解讀全部的信息,再者就算是一樣的信息,每一個人的反饋都不盡相同。數組
也就是說,價格自己就已經包含了市場參與者的錯誤預期,因此本質上市場價格總錯誤的。這或許是套利者的利潤來源。框架
根據上述原理,咱們也就知道,在一個非有效的期貨市場中,不一樣時期交割合約之間受到市場影響也並不老是同步,其訂價也並不是徹底有效的緣由。函數
那麼,根據同一種交易標的的不一樣時期交割合約價格爲基礎,若是兩個價格出現了較大的價差幅度,就能夠同時買賣不一樣時期的期貨合約,進行跨期套利。區塊鏈
與商品期貨同樣,數字貨幣也有與之相關的跨期套利合約組合。如在 OkEX 交易所中就有:ETC 當週、ETC 次周、ETC 季度。
舉個例子,假設 ETC 當週和 ETC 季度的價差長期維持在 5 左右。若是某一天價差達到 7,咱們預計價差會在將來某段時間迴歸到 5。那麼就能夠賣出 ETC 當週,同時買入 ETC 季度,來作空這個價差。反之亦然。this
儘管這種價差是存在的,可是人工操做耗時、準確性差以及價格變化的影響,人工套利每每存在諸多不肯定性。
經過量化模型捕捉套利機會並制定套利交易策略,以及程序化算法自動向交易所下達交易訂單,快速準確捕捉機會,高效穩定賺取收益,這就是量化套利的魅力所在。 spa
本篇將會教你們如何在數字貨幣交易中,利用發明者量化交易平臺和 OkEX 交易所中 ETC 期貨合約,以一個簡單的套利策略,來演示若是捕捉瞬時的套利機會,把握住每一次能夠看獲得的利潤,同時對衝有可能遇到的風險。prototype
建立一個數字貨幣跨期套利策略3d
難易度:普通級 code
策略環境
策略邏輯
上面是一個簡單的數字貨幣跨期套利策略邏輯描述,那麼如何在程序中實現本身的想法呢?咱們試着在發明者量化交易平臺先把框架搭建起來。
策略框架:
/* 全局變量 */ // 基礎數據 function Data(tradeTypeA, tradeTypeB) {} // 獲取持倉 Data.prototype.mp = function (tradeType, type) {} // 合成新K線數據和boll指標數據 Data.prototype.boll = function (num, timeCycle) {} // 下單 Data.prototype.trade = function (tradeType, type) {} // 取消訂單 Data.prototype.cancelOrders = function () {} // 處理持有單個合約 Data.prototype.isEven = function () {} // 畫圖 Data.prototype.drawingChart = function (boll) {} // 交易條件 function onTick() { var data; // 獲取基礎數據對象 data.accountData.Stocks; // 獲取帳戶餘額 data.boll; // 獲取boll指標數據 data.mp; // 獲取持倉狀態 data.cancelOrders(); // 撤單 data.drawingChart(boll); // 畫圖 data.isEven(); // 處理持有單個合約 } //入口函數 function main() { /* 交易策略預處理 */ while (true) { // 進入輪詢模式 onTick(); // 執行onTick函數 Sleep(500); // 休眠0.5秒 } }
對照着策略思路以及交易流程,能夠很輕鬆把策略框架搭建起來。整個策略能夠簡化爲三個步驟:
接下來,咱們就須要根據實際交易流程和交易細節,在策略框架裏面填充必要的細節代碼。
第1步:在全局環境中,聲明必要的全局變量。
第2步:配置策略的外部參數。
第3步:定義數據處理函數
基礎數據函數:Data ( )
建立一個構造函數 Data,並定義它的內部屬性。包括:帳戶數據、持倉數據、K線數據時間戳、套利A/B合約的買/賣一價、正/反套價差。
// 基礎數據 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; }
獲取持倉函數:mp ( )
遍歷整個持倉數組,返回指定合約、指定方向的持倉數量,若是沒有就返回 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; }
K線和指標函數:boll ( )
根據正/反套價差數據,合成新的K線序列。並返回由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指標數據 }
下單函數:trade ( )
傳入下單合約名稱和下單類型,而後以對價下單,並返回下單後的結果。因爲須要同時下兩個不一樣方向的單子,因此在函數內部根據下單合約名稱對買/賣一價作了轉換。
// 下單 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; } }
取消訂單函數:cancelOrders ( )
獲取全部未成交訂單數組,並逐個取消。而且若是有未成交的訂單就返回false,若是沒有未成交的訂單就返回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 }
處理持有單個合約:isEven ( )
在處理套利交易中出現單腿狀況,這裏直接用簡單的平掉全部倉位處理。固然,也能夠改成追單方式。
// 處理持有單個合約 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); } } }
畫圖函數:drawingChart ( )
調用 ObjChart.add ( ) 方法,在圖表中畫出必要的行情數據和指標數據:上軌、中軌、下軌、正/反套價差。
// 畫圖 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); }
第4步:在入口函數 main ( ) 裏面,執行交易前預處理代碼,這些代碼在程序啓動後,只運行一次。包括:
//入口函數 function main() { // 過濾控制檯中不是很重要的信息 SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP"); exchange.IO("currency", name + '_USDT'); //設置要交易的數字貨幣幣種 ObjChart.reset(); //程序啓動前清空以前繪製的圖表 LogProfitReset(); //程序啓動前清空以前的狀態欄信息 }
定義完上述的交易前預處理,緊接着就要進入下一個步驟,進入輪詢模式,重複執行 onTick ( ) 函數。
並設置 Sleep ( ) 輪詢時的休眠時間,由於部分數字貨幣交易所的 API 對必定時間內內置了訪問次數限制。
//入口函數 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秒 } }
第1步:獲取基礎數據對象、帳戶餘額、boll 指標數據,以供交易邏輯使用。
// 交易條件 function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // 建立一個基礎數據對象 var accountStocks = data.accountData.Stocks; // 帳戶餘額 var boll = data.boll(dataLength, timeCycle); // 獲取boll指標數據 if (!boll) return; // 若是沒有boll數據就返回 }
第1步:根據上述的策略邏輯,執行買賣操做。首先會判斷價格和指標條件是否成立,而後再判斷持倉條件是否成立,最後執行 trade ( ) 下單函數
// 交易條件 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開多 } } } }
第2步:下單完成後,須要對未成交的訂單、持有單個合約等非正常狀況作處理。以及繪製圖表。
// 交易條件 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(); // 處理持有單個合約 }
以上,咱們經過 200 多行,就把一個簡單的數字貨幣跨期套利策略完完整整的建立出來。完整的代碼以下:
// 全局變量 // 聲明一個配置圖表的 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秒 } }
本篇策略只是一個拋磚引玉,真實的實盤可不是這麼簡單,不過你能夠照着例子發揮本身天馬行空的想象。
須要提醒你們的是,以我有限的經驗來看,目前的數字貨幣市場情況,純粹的期期套利策略基本上所有不值得跑,不管是無風險的三角套利仍是跨市場套利。
緣由就在於,不管哪一個數字貨幣交易所的期貨市場,其保證金不是法幣。現現在幾乎全部的數字貨幣從今年初至今已經下跌了70%左右。也就是說策略始終都是在賺幣,可是幣價是下跌的。
放眼望去,數字貨幣市場儼然已經脫離了區塊鏈,就像當年的鬱金香同樣,價格始終來自於人們的預期和信心,信心又來源於價格…