區塊鏈量化投資系列課程(3)

NO.1

索羅斯在1987年撰寫的《金融鍊金術》 一書中,曾經提出過一個重要的命題:I believe the market prices are always wrong in the sense that they present a biased view of the future. 城市算法

市場有效假說只是理論上的假設,實際上市場參與者並不老是理性的,而且在每個時間點上,參與者不可能徹底獲取和客觀解讀全部的信息,再者就算是一樣的信息,每一個人的反饋都不盡相同。數組


也就是說,價格自己就已經包含了市場參與者的錯誤預期,因此本質上市場價格總錯誤的。這或許是套利者的利潤來源。框架

 

NO.2

根據上述原理,咱們也就知道,在一個非有效的期貨市場中,不一樣時期交割合約之間受到市場影響也並不老是同步,其訂價也並不是徹底有效的緣由。函數


那麼,根據同一種交易標的的不一樣時期交割合約價格爲基礎,若是兩個價格出現了較大的價差幅度,就能夠同時買賣不一樣時期的期貨合約,進行跨期套利。區塊鏈

量化

與商品期貨同樣,數字貨幣也有與之相關的跨期套利合約組合。如在 OkEX 交易所中就有:ETC 當週、ETC 次周、ETC 季度。
舉個例子,假設 ETC 當週和 ETC 季度的價差長期維持在 5 左右。若是某一天價差達到 7,咱們預計價差會在將來某段時間迴歸到 5。那麼就能夠賣出 ETC 當週,同時買入 ETC 季度,來作空這個價差。反之亦然。this

 

NO.3

儘管這種價差是存在的,可是人工操做耗時、準確性差以及價格變化的影響,人工套利每每存在諸多不肯定性。
經過量化模型捕捉套利機會並制定套利交易策略,以及程序化算法自動向交易所下達交易訂單,快速準確捕捉機會,高效穩定賺取收益,這就是量化套利的魅力所在。 黑板標題spa

 

本篇將會教你們如何在數字貨幣交易中,利用發明者量化交易平臺和 OkEX 交易所中 ETC 期貨合約,以一個簡單的套利策略,來演示若是捕捉瞬時的套利機會,把握住每一次能夠看獲得的利潤,同時對衝有可能遇到的風險。prototype

 

NO.4

建立一個數字貨幣跨期套利策略3d

難易度:普通級 量化交易code

 

策略環境

  • 交易標的:以太經典(ETC)
  • 價差數據:ETC 當週 - ETC 季度(省略協整性檢驗)
  • 交易週期:5 分鐘
  • 頭寸匹配:1:1
  • 交易類型:同品種跨期

 

策略邏輯

  • 作多價差開倉條件:若是當前帳戶沒有持倉,而且價差小於 boll 下軌,就作多價差。即:買開 ETC 當週,賣開 ETC 季度。
  • 作空價差開倉條件:若是當前帳戶沒有持倉,而且價差大於 boll 上軌,就作空價差。即:賣開 ETC 當週,買開 ETC 季度。
  • 作多價差平倉條件:若是當前帳戶持有 ETC 當週多單,而且持有 ETC 季度空單,而且價差大於 boll 中軌,就平多價差。即:賣平 ETC 當週,買平 ETC 季度。
  • 作空價差平倉條件:若是當前帳戶持有 ETC 當週空單,而且持有 ETC 季度多單,而且價差小於 boll 中軌,就平空價差。即:買平 ETC 當週,賣平 ETC 季度。

 

NO.5

上面是一個簡單的數字貨幣跨期套利策略邏輯描述,那麼如何在程序中實現本身的想法呢?咱們試着在發明者量化交易平臺先把框架搭建起來。

 

策略框架:

/* 
全局變量
*/

// 基礎數據
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. 下單並對後續處理。下單並對後續處理。

 

NO.6

接下來,咱們就須要根據實際交易流程和交易細節,在策略框架裏面填充必要的細節代碼。

 

1、交易前預處理

第1步:在全局環境中,聲明必要的全局變量。

  • 聲明一個配置圖表的 chart 對象 var chart = { }
  • 調用 Chart 函數,初始化圖表 var ObjChart = Chart ( chart )
  • 聲明一個空數組,用來存儲價差序列 var bars = [ ]
  • 聲明一個記錄歷史數據時間戳變量 var oldTime = 0

 

第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 ( ) 裏面,執行交易前預處理代碼,這些代碼在程序啓動後,只運行一次。包括:

  • 過濾控制檯中不是很重要的信息 SetErrorFilter ( )
  • 設置要交易的數字貨幣幣種 exchange.IO ( )
  • 程序啓動前清空以前繪製的圖表 ObjChart.reset ( )
  • 程序啓動前清空以前的狀態欄信息 LogProfitReset ( )
//入口函數
function main() {
    // 過濾控制檯中不是很重要的信息
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); //設置要交易的數字貨幣幣種
    ObjChart.reset(); //程序啓動前清空以前繪製的圖表
    LogProfitReset(); //程序啓動前清空以前的狀態欄信息
}

 

 

NO.7

定義完上述的交易前預處理,緊接着就要進入下一個步驟,進入輪詢模式,重複執行 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秒
    }
}

 

 

2、獲取並計算數據

第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數據就返回
}

 

 

3、下單並對後續處理

第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(); // 處理持有單個合約
}

 

 

NO.8

以上,咱們經過 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秒
    }
}

 

 

NO.9

本篇策略只是一個拋磚引玉,真實的實盤可不是這麼簡單,不過你能夠照着例子發揮本身天馬行空的想象。
須要提醒你們的是,以我有限的經驗來看,目前的數字貨幣市場情況,純粹的期期套利策略基本上所有不值得跑,不管是無風險的三角套利仍是跨市場套利。 BTC價格趨勢
緣由就在於,不管哪一個數字貨幣交易所的期貨市場,其保證金不是法幣。現現在幾乎全部的數字貨幣從今年初至今已經下跌了70%左右。也就是說策略始終都是在賺幣,可是幣價是下跌的。

 

放眼望去,數字貨幣市場儼然已經脫離了區塊鏈,就像當年的鬱金香同樣,價格始終來自於人們的預期和信心,信心又來源於價格…

 

原文連接:https://quant.la/Article/View/816

相關文章
相關標籤/搜索