量化對衝網格模型基於發明者平臺的比特幣OKCoin期貨交易策略

OKCoin期貨跨期對衝策略,季度、當週、次周api

一、 季度-當週數組

二、 季度-次周bash

三、 當週-次周markdown

四、在週五交割前5分鐘會自動 平倉, 鎖定15分鐘 後再正常運行。tcp


做者本人呢 是菜鳥程序猿一個, 平時沒什麼愛好,就喜歡寫代碼。最近一年在發明者量化上學到了很多新知識, 模仿Zero 大神的商品期貨對衝網格策略,寫了一個 電子貨幣 (BTC期貨) 的對衝網格策略。回測一下還行(固然回測只是最初步的檢測,不表明任何結論!)函數


如下代碼不能直接運行,須要配置參數(在發明者量化平臺)。代碼鏈接: www.botvs.com/strategy/34…oop

對衝網格策略


對衝網格策略


var checkTime = 0;
var residualTime = 0;
var checkPreTime = 0;
var JGDate = [];                                     // 交割模擬。 ["BTC1129", "BTC1229", "BTC0316"];
var JGDateIndex = 0;                                 // 模擬交割索引。
var JGHoursCorrect = 8;                              // 檢測交割剩餘小時,在實盤中須要修正 8小時。 模擬時該值設置爲0
var idA = null;
var idB = null;
var A = 1;
var B = 2;
var PRE = 3;
var PLUS = 4;
var MINUS = 5;
var FREE = 6;

function init(){
    if(istry){
        JGHoursCorrect = 0;                          // 模擬中 該值修改成0
        Log("啓用模擬 交割,初始化交割日期列表。");
        UpdateJGDate();
    }
}

function UpdateJGDate(){                             
    // 循環31次 遍歷出 當前月份的全部 星期5的 日期, 取時間戳, 對比當前時間戳,小於當前的 丟棄,大於當前的第一個便是 
    // 在用時間戳恢復日期,拿到 字符串
    var array_JG_stamp = [];
    var date = new Date();
    var nowStamp = date.getTime();
    var nowMonth = date.getMonth();
    var nextJG_Stamp = 0;
    date.setHours(16);                     // 0~23
    date.setMinutes(0);                    // 0~59
    date.setSeconds(0);                    // 0~59
    for(var i = 1 ; i <= 31; i++){
        date.setDate(i);
        var EveryDay = date.getDay();        // 1~6
        if(EveryDay === 5 && nowMonth == date.getMonth()){
            var fridayStamp = date.getTime();
            array_JG_stamp.push(fridayStamp);
        }
    }
    for(var j = 0 ; j < array_JG_stamp.length ; j++){
        if(nowStamp <= array_JG_stamp[j]){
            nextJG_Stamp = array_JG_stamp[j];
            break;
        }
        if(j == array_JG_stamp.length - 1){     
            nextJG_Stamp = array_JG_stamp[j] + 1000 * 7 * 24 * 60 * 60;       
        }
    }
    array_JG_stamp = [];
    for(var n = 1 ; n <= 100; n++){  // 生成 100 個 模擬交割日期。
        if(n == 1){
            array_JG_stamp.push(nextJG_Stamp);
        }else{
            nextJG_Stamp += 1000 * 7 * 24 * 60 * 60;
            array_JG_stamp.push(nextJG_Stamp);
        }
    }
    var date2 = new Date();
    for(var m = 0 ; m < array_JG_stamp.length ; m++){
        date2.setTime(array_JG_stamp[m]);
        var strMonth = date2.getMonth() + 1;
        var strDate = date2.getDate();
        if(strMonth < 10){
            strMonth = '0' + strMonth;
        }
        if(strDate < 10){
            strDate = '0' + strDate;
        }
        JGDate.push("BTC" + strMonth + strDate);
    }
    Log("模擬生成的交割日期:", JGDate);
}

function CheckDelivery(nowTime, Symbol, task) {
    var contractInfo = null;
    if(checkTime <= 0){
        var contractName = "";
        var ContractIndex = 0;
        if(Symbol === "this_week"){
            ContractIndex = 0;
        }else if(Symbol === "next_week"){
            ContractIndex = 1;
        }else if(Symbol === "quarter"){
            ContractIndex = 2;
        }
        if(istry === true){                          // 判斷是否是 模擬測試
            try{
                contractName = JGDate[JGDateIndex];  // 模擬測試 交割日期
                JGDateIndex++;
            } catch(e){
                Log("回測模式,更新交割日期錯誤", e);
                JGDateIndex--;
            }
        }
        while (contractName == "") {
            //var contractInfo = HttpQuery("https://www.okcoin.com/api/v1/future_hold_amount.do?symbol=btc_usd&contract_type=this_week"); //只是檢測this_week ,避免重複調用提升效率
            switch(ContractIndex){
                case 0: contractInfo = HttpQuery("https://www.okcoin.com/api/v1/future_hold_amount.do?symbol=btc_usd&contract_type=this_week");
                        break;
                case 1: contractInfo = HttpQuery("https://www.okcoin.com/api/v1/future_hold_amount.do?symbol=btc_usd&contract_type=next_week");
                        break;
                case 2: contractInfo = HttpQuery("https://www.okcoin.com/api/v1/future_hold_amount.do?symbol=btc_usd&contract_type=quarter");
                        break;
                default: Log("contractInfo:", contractInfo); 
                         //throw "switch NumContractType Error!";
            }            //根據 contractType 類型 選擇讀取合約交割日期
            if (!contractInfo || contractInfo.length === 0) {
                Sleep(100);
                continue;
            }
            try {
                contractName = (JSON.parse(contractInfo))[0].contract_name;
            } catch (e) {
                Log("CheckDelivery Error",contractInfo, e);
                return 0;
            }
        }
        var nowDateTime = new Date();
        contractName = contractName.split("BTC")[1]; //抽取BTC後的字符串 從新賦值
        var strMonth = contractName[0] + contractName[1];
        var strDay = contractName[2] + contractName[3];
        var strYear = nowDateTime.getFullYear() + ""; //獲取 年份字符串
        
        // 處理跨年問題
        var nowMonth = nowDateTime.getMonth(); // 獲取月份
        if(strMonth < nowMonth){
            strYear = (strYear - 0) + 1;
            strYear = strYear + "";
        }
        
        var strDate = strYear + '-' + strMonth + '-' + strDay + ' ' + "16:00:00";
        var deliveryTime = (new Date(strDate)).getTime();             // + 16 * 60 * 60 * 1000;
        nowTime = nowDateTime.getTime();
        residualTime = (deliveryTime - nowTime) / 1000 / 60 / 60 - JGHoursCorrect; //單位是小時
        checkTime = (deliveryTime - nowTime) - JGHoursCorrect * 1000 * 60 * 60;    //還差多少毫秒交割
        checkPreTime = nowTime;                                       //記錄開始的時間
        Log("合約", Symbol, "交割日期獲取:", strDate); 
    }else{
        checkTime -= nowTime - checkPreTime;                          //減去消耗的時間
        checkPreTime = nowTime;
        residualTime = checkTime / 1000 / 60 / 60;                    // 計算距離交割多少小時
    }
    if(residualTime < 24){                                            //交割小於24小時
        MSG_String = "&emsp;&emsp;" + Symbol + " 交割時間剩餘:" + residualTime + "小時!!#FF0000";
        if((checkTime / 1000 / 60) <= 5 && task.isFrozen == false){   // 每次交割前5分鐘 鎖定。
            Log("距離交割剩餘5分鐘,平掉全部倉位!#FF0000");
            for(var index = 0; index < task.dic.length; index++){
                if(task.dic[index].hold > 0){                                                 // 平空A 平多B
                    task.action = [index, "closesell", "closebuy", task.dic[index].hold];
                    Log(JSON.stringify(task.action));
                    Hedge_Open_Cover(task);
                    $.PlotFlag(nowTime, "C", "closesell-closebuy", "circlepin");
                }else if(task.dic[index].hold < 0){                                           // 平多A 平空B
                    task.action = [index, "closebuy", "closesell", task.dic[index].hold];
                    Log(JSON.stringify(task.action));
                    Hedge_Open_Cover(task);
                    $.PlotFlag(nowTime, "C", "closebuy-closesell", "circlepin");
                }
            }
            task.nowAccount = _C(task.e.GetAccount);
            var Positions = _C(task.e.GetPosition);
            UpdatePosition(task, Positions);
            Log("所有倉位已平,檢查持倉:", Positions, "程序凍結15分鐘!#FF0000");
            task.isFrozen = true;
            task.FrozenStartTime = new Date().getTime();
        }
    }else{
        MSG_String = "&emsp;&emsp;" + Symbol + " 交割時間剩餘:" + residualTime + "小時!!";
    }
    return _N(residualTime, 3);
}


function OpenPriceToActual(Price, Piece){
    var OnePieceEquivalentCoin = 100 / Price;
    var OpenFee = Piece * (OnePieceEquivalentCoin) * (0.03 * 0.01);
    var Actual = Price + (OpenFee * Price) / (OnePieceEquivalentCoin * Piece);
    return Actual;
    //Log("Actual:", Actual, "OpenFee:", OpenFee, "OnePieceEquivalentCoin:", OnePieceEquivalentCoin, "保證金:", OpenFee / 0.01 / 0.03 / 10); // 測試
    //var Actual = Price + ((0.03 * 0.01) * Price);
}

function CreateHedgeList(Begin, End, Size, Step, AmountOfPoint, SymbolA, SymbolB){
    // "(this_week&quarter)100:90:1;110:100:1;120:110:1;130:120:1;140:130:1;150:140:1;160:150:1;170:160:1;180:170:1;190:180:1";
    if((SymbolA !== "this_week" && SymbolA !== "next_week" && SymbolA !== "quarter") || (SymbolB !== "this_week" && SymbolB !== "next_week" && SymbolB !== "quarter")){
        throw "合約代碼錯誤: SymbolA " + SymbolA + " SymbolB " + SymbolB;
    }
    var BodyString = "";
    var HeadString = '(' + SymbolA + '&' + SymbolB + ')';
    for(var i = Begin ; i <= End ; i += Step){
        if(i + Step > End){
            BodyString += (i + ':') + (i - Size) + (':' + AmountOfPoint);
        }else{
            BodyString += (i + ':') + (i - Size) + (':' + AmountOfPoint) + ';';
        }
    }
    var HL = HeadString + BodyString;
    Log("按參數生成對衝列表:", HL);
    return HL;
}

function UpdatePosition(task, Positions, onlyAorBorPRE){
    if(Positions.length > 2){
        Log(Positions, "Positions 長度大於2!#FF0000");
        throw "同類型合約不能同時持有多倉空倉。";
    }
    if(onlyAorBorPRE == PRE){
        task.Pre_APositions = task.APositions;
        task.Pre_BPositions = task.BPositions;
    }
    for(var i = 0; i < Positions.length; i++){
        if(Positions[i].ContractType == task.symbolA && onlyAorBorPRE !== B){
            task.APositions = Positions[i];
        }
        if(Positions[i].ContractType == task.symbolB && onlyAorBorPRE !== A){
            task.BPositions = Positions[i];
        }
    }
    if(Positions.length == 0){
        if(onlyAorBorPRE !== B){
            task.APositions = {MarginLevel: 0, Amount: 0, FrozenAmount: 0, Price: 0, Profit: 0, Type: 0, ContractType: ""};
        }
        if(onlyAorBorPRE !== A){
            task.BPositions = {MarginLevel: 0, Amount: 0, FrozenAmount: 0, Price: 0, Profit: 0, Type: 0, ContractType: ""};
        }
        if(onlyAorBorPRE !== A && onlyAorBorPRE !== B){
            task.APositions = {MarginLevel: 0, Amount: 0, FrozenAmount: 0, Price: 0, Profit: 0, Type: 0, ContractType: ""}; 
            task.BPositions = {MarginLevel: 0, Amount: 0, FrozenAmount: 0, Price: 0, Profit: 0, Type: 0, ContractType: ""};
        }
    }
}

function DealAction(task, AorB, amount){
    if(amount <= 0){
        throw "錯誤: DealAction 的 amount 參數爲:" + amount;
    }
    if(AorB == A){
        task.e.SetContractType(task.symbolA);
        task.e.SetDirection(task.action[1]);
        if(task.action[1] == "buy" || task.action[1] == "closesell"){
            idA = task.e.Buy(-1, typeof(amount) == "undefined" ? Math.abs(task.action[3]) : amount, task.symbolA);
        }else if(task.action[1] == "sell" || task.action[1] == "closebuy"){
            idA = task.e.Sell(-1, typeof(amount) == "undefined" ? Math.abs(task.action[3]) : amount, task.symbolA);
        }
    }
    if(AorB == B){
        task.e.SetContractType(task.symbolB);
        task.e.SetDirection(task.action[2]);
        if(task.action[2] == "buy" || task.action[2] == "closesell"){
            idB = task.e.Buy(-1, typeof(amount) == "undefined" ? Math.abs(task.action[3]) : amount, task.symbolB);
        }else if(task.action[2] == "sell" || task.action[2] == "closebuy"){
            idB = task.e.Sell(-1, typeof(amount) == "undefined" ? Math.abs(task.action[3]) : amount, task.symbolB);
        }
    }
}

function DealActionLimit(task, AorB, Piece, isAgain){
    var tradeInfo  = null;
    var Price = 0;
    var Piece = Math.abs(Piece);
    if(isAgain){
        while(true){
            task.e.SetContractType(task.symbolA);
            var Adepth = task.e.GetDepth();
            task.e.SetContractType(task.symbolB);
            var Bdepth = task.e.GetDepth();
            
            if(!Adepth || !Bdepth || Adepth.Asks.length === 0 || Adepth.Bids.length === 0 || Bdepth.Asks.length === 0 || Bdepth.Bids.length === 0){
                //Log("獲取深度信息異常!", Adepth, Bdepth);
                Sleep(Interval);
                continue;
            }
            
            task.Adepth = Adepth;
            task.Bdepth = Bdepth;
            
            task.Adiff = _N(Adepth.Bids[0].Price - Bdepth.Asks[0].Price);
            task.Bdiff = _N(Adepth.Asks[0].Price - Bdepth.Bids[0].Price);
            break;
        }
    }
    task.e.SetContractType(AorB == A ? task.symbolA : task.symbolB);
    task.e.SetDirection(task.action[AorB]);
    if(task.action[AorB] == "buy"){
        Price = AorB == A ? task.Adepth.Asks[0].Price : task.Bdepth.Asks[0].Price;
        tradeInfo = task.P.OpenLong(AorB == A ? task.symbolA : task.symbolB, Piece, Price);
    }else if(task.action[AorB] == "sell"){
        Price = AorB == A ? task.Adepth.Bids[0].Price : task.Bdepth.Bids[0].Price;
        tradeInfo = task.P.OpenShort(AorB == A ? task.symbolA : task.symbolB, Piece, Price);
    }else if(task.action[AorB] == "closebuy"){
        Price = AorB == A ? task.Adepth.Bids[0].Price : task.Bdepth.Bids[0].Price;
        tradeInfo = task.P.Cover(AorB == A ? task.symbolA : task.symbolB, Price, Piece, PD_LONG); // task.action[AorB] 
    }else if(task.action[AorB] == "closesell"){
        Price = AorB == A ? task.Adepth.Asks[0].Price : task.Bdepth.Asks[0].Price;
        tradeInfo = task.P.Cover(AorB == A ? task.symbolA : task.symbolB, Price, Piece, PD_SHORT); // task.action[AorB]
    }
    if((task.action[AorB] == "buy" || task.action[AorB] == "sell") && tradeInfo.amount === 0){
        return false;
    }else if((task.action[AorB] == "closebuy" || task.action[AorB] == "closesell") && tradeInfo == 0){
        return false;
    }
    return tradeInfo;
}

function CalcActualPrice(task){
    if(task.action[0] == 0){
        var A_Pv = task.APositions.Price;
        var B_Pv = task.BPositions.Price; 
        task.dic[task.action[0]].ActualPrice = A_Pv - B_Pv;
        Log(task.symbolA, A_Pv, task.symbolB, B_Pv, "#FF0000");
    }else if(task.APositions.Amount == 1 && task.BPositions.Amount == 1){
        var A_Pv = task.APositions.Price;
        var B_Pv = task.BPositions.Price; 
        task.dic[task.action[0]].ActualPrice = A_Pv - B_Pv;
        Log(task.symbolA, A_Pv, task.symbolB, B_Pv, "#FF0000");
    }else{
        var A_Pv = task.APositions.Price;
        var A_P1 = task.Pre_APositions.Price;
        var A_m = task.APositions.Amount - task.Pre_APositions.Amount;
        var A_n = task.Pre_APositions.Amount;

        var B_Pv = task.BPositions.Price; 
        var B_P1 = task.Pre_BPositions.Price;
        var B_m = task.BPositions.Amount - task.Pre_BPositions.Amount;
        var B_n = task.Pre_BPositions.Amount;

        var A_P2 = A_Pv * A_P1 * A_m / ((A_m + A_n) * A_P1 - A_n * A_Pv);
        var B_P2 = B_Pv * B_P1 * B_m / ((B_m + B_n) * B_P1 - B_n * B_Pv);

        task.dic[task.action[0]].ActualPrice = A_P2 - B_P2;
        Log(task.symbolA, A_P2, task.symbolB, B_P2, "#FF0000");
    }
}

function Hedge_Open_Cover(task){
    if(task.isUseMarketOrder === false){       // 限價單
        if((task.action[1] === "buy" && task.action[2] === "sell") || (task.action[1] === "sell" && task.action[2] === "buy")){ // open
            var tradeInfo_A = DealActionLimit(task, A, task.action[3]);
            var dealAmount_A = Math.abs(task.action[3]);
            while(tradeInfo_A == false || (dealAmount_A -= tradeInfo_A.amount) !== 0){
                Log("合約:" + task.symbolA + "已對衝" + (tradeInfo_A == false ? 0 : tradeInfo_A.amount), "剩餘,重試!張數:" + dealAmount_A);
                tradeInfo_A = DealActionLimit(task, A, dealAmount_A, true);
                Sleep(Interval);
            }

            var tradeInfo_B = DealActionLimit(task, B, task.action[3]);
            var dealAmount_B = Math.abs(task.action[3]);
            while(tradeInfo_B == false || (dealAmount_B -= tradeInfo_B.amount) !== 0){
                Log("合約:" + task.symbolB + "已對衝" + (tradeInfo_B == false ? 0 : tradeInfo_B.amount), "剩餘,重試!張數:" + dealAmount_B);
                tradeInfo_B = DealActionLimit(task, B, dealAmount_B, true);
                Sleep(Interval);
            }

            task.dic[task.action[0]].hold = task.action[3];
            // task.dic[task.action[0]].ActualPrice = _N(orderA.AvgPrice - orderB.AvgPrice);
        }else if((task.action[1] === "closesell" && task.action[2] === "closebuy") || (task.action[1] === "closebuy" && task.action[2] === "closesell")){ // cover
            var tradeInfo_A_Piece = DealActionLimit(task, A, task.action[3]);
            var dealAmount_A = Math.abs(task.action[3]);
            while(tradeInfo_A_Piece == false || (dealAmount_A -= tradeInfo_A_Piece) !== 0){
                Log("合約:" + task.symbolA + "已對衝" + (tradeInfo_A == false ? 0 : tradeInfo_A_Piece), "剩餘,重試!張數:" + dealAmount_A);
                tradeInfo_A_Piece = DealActionLimit(task, A, dealAmount_A, true);
                Sleep(Interval);
            }
            
            var tradeInfo_B_Piece = DealActionLimit(task, B, task.action[3]);
            var dealAmount_B = Math.abs(task.action[3]);
            while(tradeInfo_B_Piece == false || (dealAmount_B -= tradeInfo_B_Piece) !== 0){
                Log("合約:" + task.symbolB + "已對衝" + (tradeInfo_B == false ? 0 : tradeInfo_B_Piece), "剩餘,重試!張數:" + dealAmount_B);
                tradeInfo_B_Piece = DealActionLimit(task, B, dealAmount_B, true);
                Sleep(Interval);
            }

            task.dic[task.action[0]].hold = 0;
            task.dic[task.action[0]].ActualPrice = 0;
            task.dic[task.action[0]].CoverTimes += 1;
        }
        Sleep(100);
    }else if(task.isUseMarketOrder === true){  // 市價單
        var orderA = null;
        var orderB = null;
        if((task.action[1] === "buy" && task.action[2] === "sell") || (task.action[1] === "sell" && task.action[2] === "buy")){ // open
            DealAction(task, A);
            DealAction(task, B);

            while(!idA || !idB){
                if(!idA){
                    DealAction(task, A);
                }
                if(!idB){
                    DealAction(task, B);
                }
            }
            
            while(true){
                if(!orderA || orderA.Status !== ORDER_STATE_CLOSED){
                    orderA = task.e.GetOrder(idA);
                }
                if(!orderB || orderB.Status !== ORDER_STATE_CLOSED){
                    orderB = task.e.GetOrder(idB);
                }
                if(orderA && orderB && orderA.Status == ORDER_STATE_CLOSED && orderB.Status == ORDER_STATE_CLOSED){
                    break;
                }
                Sleep(Interval);
            }

            //Log("Open symbolA:", orderA, "symbolB:", orderB);
            task.dic[task.action[0]].hold = task.action[3];
            task.dic[task.action[0]].ActualPrice = _N(orderA.AvgPrice - orderB.AvgPrice);
        }else if((task.action[1] === "closesell" && task.action[2] === "closebuy") || (task.action[1] === "closebuy" && task.action[2] === "closesell")){ // cover
            DealAction(task, A);
            DealAction(task, B);

            while(!idA || !idB){
                if(!idA){
                    DealAction(task, A);
                }
                if(!idB){
                    DealAction(task, B);
                }
            }

            while(true){
                if(!orderA || orderA.Status !== ORDER_STATE_CLOSED){
                    orderA = task.e.GetOrder(idA);
                }
                if(!orderB || orderB.Status !== ORDER_STATE_CLOSED){
                    orderB = task.e.GetOrder(idB);
                }
                if(orderA && orderB && orderA.Status == ORDER_STATE_CLOSED && orderB.Status == ORDER_STATE_CLOSED){
                    break;
                }
                Sleep(Interval);
            }

            //Log("Cover symbolA:", orderA, "symbolB:", orderB);
            task.dic[task.action[0]].hold = 0;
            task.dic[task.action[0]].ActualPrice = 0;
            task.dic[task.action[0]].CoverTimes += 1;
        }
        Sleep(100); // 避免訪問過於頻繁。
    }
}

function TasksController(){ // 任務控制器 構造函數
    // 構造用於返回的 控制器對象
    var self = {};
    self.tasks = []; // 控制器的任務數組。

    self.AddTask = function(e, symbolA, symbolB, HedgeList){ // 添加對衝 任務, 函數中進行 構造, 部分參數變量未初始化。在main 函數開始 根據界面參數列表,肯定task 個數 進行構造。
        var task = {
            // 任務的參數變量
            e : e,
            symbolA : symbolA,
            symbolB : symbolB,
            HedgeList : HedgeList,
            dic : [],
            Adepth : null,
            Bdepth : null,
            Adiff : 0,
            Bdiff : 0,
            action : null,
            lastUpdateLineTime : 0,
            MarginLevel : MarginLevel,
            isUseMarketOrder : true,
            // 帳戶信息
            initAccount : _C(e.GetAccount),
            nowAccount : null,
            isFrozen : false,
            FrozenStartTime : 0,
            APositions : null,
            BPositions : null,
            Pre_APositions : null,
            Pre_BPositions : null,
            PositionsLastUpdateTime : 0,
            isLogProfit : false,
            taskProfit : 0,
            State : FREE,
        };
        self.tasks.push(task);
    }
    
    self.InitTask = function(){ // 初始化 任務,設置合約 槓桿等
        _.each(self.tasks, function(task){
            var arr = task.HedgeList.split(';');
            var hedgeDirection = 1;
            // 可擴展 恢復持倉,讀取當前持倉。
            _.each(arr, function(pair){
                var tmp = pair.split(':');                               // 把每一個 網格節點的 單元 按照':'字符 分割成 參數 數組 tmp
                if (tmp.length != 3) {                                   // 因爲 格式 是  30:15:1  即   開倉差價: 平倉差價: 下單量    ,因此 用 ':' 分割後 tmp長度 不是3的 即 格式錯誤
                    throw "開倉表不正確";                                  // 拋出異常  格式錯誤
                }
                var st = {                                               // 每次迭代的時候 構造一個對象 
                    open: Number(tmp[0]),                                // 開倉  把 tmp[0]  即 30:15:1 按 ':' 分割後 生成的數組中的 第一個元素  30 , 經過 Number 函數 轉換爲數值
                    cover: Number(tmp[1]),                               // 平倉..
                    amount: Number(tmp[2]),                              // 量..
                    hold: 0,                                             // 持倉 初始爲0
                    ActualPrice: 0,
                    PointProfit : 0,                                     // 但個倉位累計差價。       
                    CoverTimes : 0,                                      // 平倉次數      
                };
                task.dic.push(st);
            });
            // 處理其它 初始化工做
            task.P = $.NewPositionManager(task.e);
            task.isUseMarketOrder = isUseMarketOrder;                    // 初始是否使用市價單
            task.e.SetMarginLevel(task.MarginLevel);                     // 設置槓桿
            task.nowAccount = _C(task.e.GetAccount);                     // 初始化 當前帳戶信息
            // 檢查, 啓用的網格 是否會 耗盡資金
            if((task.dic[0].open < 0 && isCreateHedgeList) || (task.dic[0].cover < 0 && isCreateHedgeList)){      
                // throw "自動網格生成錯誤:第一個節點開倉值、平倉值 小於0,會引發產生重複部分。" +JSON.stringify(task.dic);        
            }
            var dic_length = task.dic.length;
            var sumPiece = 0;
            for(var i = 0; i < dic_length; i++){
                sumPiece += task.dic[i].amount;
            }
            sumPiece = sumPiece * 2;
            task.e.SetContractType(task.symbolA);
            var tickerA = _C(task.e.GetTicker);
            task.e.SetContractType(task.symbolB);
            var tickerB = _C(task.e.GetTicker);
            var Margin = sumPiece * 100 * OK_Rate / ((tickerA.Last + tickerB.Last) / 2) / task.MarginLevel;
            if(MarginRatio * task.initAccount.Stocks < Margin){
                if(istry == false || IsVirtual()){
                    throw "滿倉所需保證金:" + Margin + " 超出 限制比例: " + MarginRatio;
                }else{
                    Log("注意,實盤測試模式開啓!#FF0000");
                }
            } 
            Log("按照當前初始化,滿倉將開" + sumPiece + "張合約,共須要 保證金:" + Margin + "BTC");
        });
    }

    self.DealTask = function(nowTime){
        // 處理task 任務
        tbls = []; // 每次初始清空
        _.each(self.tasks, function(task){
            // 迭代處理 task 
            task.e.SetContractType(task.symbolA);
            var Adepth = task.e.GetDepth();
            task.e.SetContractType(task.symbolB);
            var Bdepth = task.e.GetDepth();
            
            if(!Adepth || !Bdepth || Adepth.Asks.length === 0 || Adepth.Bids.length === 0 || Bdepth.Asks.length === 0 || Bdepth.Bids.length === 0){
                return;
            }
            
            task.Adepth = Adepth;
            task.Bdepth = Bdepth;
            
            task.Adiff = _N(Adepth.Bids[0].Price - Bdepth.Asks[0].Price);
            task.Bdiff = _N(Adepth.Asks[0].Price - Bdepth.Bids[0].Price);
            
            if(nowTime - task.lastUpdateLineTime > 1000 * 60){
                $.PlotLine("plus", task.Adiff);
                $.PlotLine("minus", task.Bdiff);
                task.lastUpdateLineTime = nowTime;
            }

            // 判斷解除凍結
            if(nowTime - task.FrozenStartTime > 1000 * 15 * 60 && task.isFrozen == true){
                task.isFrozen = false;
                if(For_USD == false){
                    var lastRate = OK_Rate;
                    OK_Rate = task.e.GetUSDCNY();
                    task.e.SetRate(OK_Rate);
                }
                Log("凍結解除!", For_USD == false ? "而且更新匯率,上次匯率:" + lastRate + "更新後匯率:" + OK_Rate + "#FF0000" : "#FF0000");
            }

            // 交易邏輯
            var SumHold = task.dic.length;
            for(var index = 0; index < task.dic.length && task.isFrozen == false; index++){
                if(task.dic[index].hold === 0){
                    if(task.dic[index].open <= task.Adiff && task.State !== MINUS){                            // 正對衝 空A 多B
                        task.action = [index, "sell", "buy", task.dic[index].amount];
                        Log(JSON.stringify(task.action));
                        Hedge_Open_Cover(task);

                        $.PlotFlag(nowTime, "O", "sell-buy", "flag", "red");
                        task.isLogProfit = false;
                        task.nowAccount = _C(task.e.GetAccount);        
                        var Positions = _C(task.e.GetPosition);  // 獲取持倉信息。
                        UpdatePosition(task, Positions, PRE);
                        Log("Open symbolA:", task.APositions, "symbolB:", task.BPositions);
                        CalcActualPrice(task);
                        task.State = PLUS;
                    }else if(task.dic[index].open <= -task.Bdiff && task.State !== PLUS){                     // 反對衝 多A 空B
                        task.action = [index, "buy", "sell", -task.dic[index].amount];
                        Log(JSON.stringify(task.action));
                        Hedge_Open_Cover(task);

                        $.PlotFlag(nowTime, "O", "buy-sell", "flag", "red");
                        task.isLogProfit = false;
                        task.nowAccount = _C(task.e.GetAccount);        
                        var Positions = _C(task.e.GetPosition);  // 獲取持倉信息。
                        UpdatePosition(task, Positions, PRE);
                        Log("Open symbolA:", task.APositions, "symbolB:", task.BPositions);
                        CalcActualPrice(task);
                        task.State = MINUS;
                    }else{
                        SumHold--;
                    }
                }else{
                    if(task.dic[index].hold > 0 && task.dic[index].cover >= task.Bdiff){  // 平空A 平多B
                        task.action = [index, "closesell", "closebuy", task.dic[index].hold];
                        Log(JSON.stringify(task.action));
                        // Buy_SymbolA_Sell_SymbolB(task);
                        Hedge_Open_Cover(task);
                        $.PlotFlag(nowTime, "C", "closesell-closebuy", "circlepin");
                        task.nowAccount = _C(task.e.GetAccount);
                        var Positions = _C(task.e.GetPosition);  // 獲取持倉信息。
                        UpdatePosition(task, Positions);
                        Log("Cover symbolA:", task.APositions, "symbolB:", task.BPositions);
                    }else if(task.dic[index].hold < 0 && task.dic[index].cover >= -task.Adiff){  // 平多A 平空B
                        task.action = [index, "closebuy", "closesell", task.dic[index].hold];
                        Log(JSON.stringify(task.action));
                        // Sell_SymbolA_Buy_SymbolB(task);
                        Hedge_Open_Cover(task);
                        $.PlotFlag(nowTime, "C", "closebuy-closesell", "circlepin");
                        task.nowAccount = _C(task.e.GetAccount);
                        var Positions = _C(task.e.GetPosition);  // 獲取持倉信息。
                        UpdatePosition(task, Positions);
                        Log("Cover symbolA:", task.APositions, "symbolB:", task.BPositions);
                    }
                }
            }

            if(SumHold != 0 && nowTime - task.PositionsLastUpdateTime > 1000 * 30){
                var Positions = _C(task.e.GetPosition);  // 獲取持倉信息。
                UpdatePosition(task, Positions);
                task.PositionsLastUpdateTime = nowTime;
            }else if(SumHold == 0 && task.isLogProfit == false){
                sumProfit = 0;
                task.State = FREE;
                for(var a = 0 ; a < TC.tasks.length; a++){
                    sumProfit += TC.tasks[a].taskProfit;
                }
                LogProfit(sumProfit, "當前:", task.nowAccount, "初始:", task.initAccount);
                task.taskProfit = task.nowAccount.Stocks - task.initAccount.Stocks;
                task.isLogProfit = true;
            }

            // 檢測交割剩餘時間、發送 交割前平倉命令
            CheckDelivery(nowTime, "this_week", task);  // 檢測 當週交割。 必須在當週交割前平倉。

            // 測試 將數據如今狀態欄
            var tbl = {
                type : "table",
                title : task.symbolA + "&" + task.symbolB,
                cols : ["key", "value"],
                rows : []
            };
            for(var key in task){
                value = task[key];
                if(key === "dic" || key === "Adepth" || key === "Bdepth" || key === "initAccount" || key === "nowAccount" || key === "isFrozen" || 
                    key === "FrozenStartTime" || key === "P" || key === "Pre_APositions" || key === "Pre_BPositions"){ // 過濾顯示
                    continue;
                }
                if(key === "e"){
                    value = "初始: " + JSON.stringify(task.initAccount) + " 當前: " + JSON.stringify(task.nowAccount);
                    key = task[key].GetName();
                }
                tbl.rows.push([key, value]);
            }
            // 判斷下網格方向
            if(task.dic[0].hold < 0){
                for(var j = 0; j < task.dic.length; j++){
                    var task_dic = JSON.stringify(task.dic[j]);
                    if(task.dic[j].hold > 0){
                        task_dic += "#FF0000";
                    }else if(task.dic[j].hold < 0){
                        task_dic += "#32CD32";
                    }
                    tbl.rows.push([j, task_dic]);
                }
            }else if(task.dic[0].hold > 0){
                for(var j = task.dic.length - 1; j >= 0; j--){
                    var task_dic = JSON.stringify(task.dic[j]);
                    if(task.dic[j].hold > 0){
                        task_dic += "#FF0000";
                    }else if(task.dic[j].hold < 0){
                        task_dic += "#32CD32";
                    }
                    tbl.rows.push([j, task_dic]);
                }
            }else{
                for(var j = 0; j < task.dic.length; j++){
                    var task_dic = JSON.stringify(task.dic[j]);
                    if(task.dic[j].hold > 0){
                        task_dic += "#FF0000";
                    }else if(task.dic[j].hold < 0){
                        task_dic += "#32CD32";
                    }
                    tbl.rows.push([j, task_dic]);
                }
            }
            tbls.push(tbl);
        });
        if(tbls.length === 0){ // 容錯模式,或者實盤中 可能有緣由致使 _.each 迭代中 返回,tbls 爲  [] ,處理此種狀況。
            return;
        }
        LogStatus("輪詢耗時:" + StrLoopTime + " 當前時間:" + _D() + MSG_String + '\n`' + JSON.stringify(tbls) + '`'); // 輸出到狀態欄
    }

    // 返回對象
    return self;
}

var TC = null; // TasksController 對象
var tbls = [];
var MSG_String = "&emsp;&emsp;";
var StrLoopTime = "";
var OK_Rate = 0;
var SumUseTime = 0;
var times = 0;
var sumTimes = 0;
var sumProfit = 0;
function main(){
    // 測試代碼 模擬界面參數
    if(isFilter){
        Log("啓用過濾常規錯誤信息。");
        SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
    }

    var Positions = [];
    for(var j = 0 ; j < exchanges.length ; j++){
        var position = _C(exchanges[j].GetPosition);
        if(position.length !== 0){
            Positions.push(position);
        }
    }
    if(AutoRecoveryState == false && Positions.length == 0){  // 沒有持倉,而且關閉了自動恢復
        if(isCreateHedgeList == true){
            HedgeTable = CreateHedgeList(begin, end, size, step, amountOfPoint, symbolA, symbolB);
        }

        // 輸出對衝控制表,並檢查。
        Log("對衝控制表", HedgeTable);
        if(HedgeTable == "(this_week&quarter)30:15:1"){
            Log("注意!對衝控制表 當前爲 默認值!可能引發虧損。");
        }
    
        // 建立控制器對象
        TC = TasksController();
    
        var exchangesNum = 0;
        var e_index = 0;
        for(var i = 0; i < exchanges.length; i++){ // 遍歷交易所對象數組
            exchangesNum++;
        }

        var arr = HedgeTable.split('(');

        _.each(arr, function(item) {
            if (item != '') {
                var tmp = item.split(')');
                var pair = tmp[0].replace('(', '').split('&');
                // Log("pair:", pair, "tmp:", tmp); // 測試 顯示 處理後的 控制表字符串
                TC.AddTask(exchanges[e_index], pair[0], pair[1], tmp[1]);
                if(For_USD){                                                     // 設置匯率
                    OK_Rate = 1;
                    exchanges[e_index].SetRate(1);
                    Log("使用的價格爲美圓計價,匯率:", OK_Rate, ",請注意 」對衝 控制表「 是否爲美圓差價#FF0000"); // 幣種提示
                }else{
                    OK_Rate = exchanges[e_index].GetUSDCNY();
                    exchanges[e_index].SetRate(OK_Rate);
                    Log("使用的價格爲RMB計價,匯率:", OK_Rate, ",請注意 」對衝 控制表「 是否爲RMB差價#FF0000");  // 幣種提示
                }
                if(--exchangesNum < 0){
                    throw "添加的交易所 和 對衝控制表 不匹配!交易所個數:" + exchanges.length + " arr:" + arr;
                }else if(exchangesNum !== 0){
                    e_index++;
                }
            }
        });
    
        Log("交易所對象最大索引 e_index:", e_index, "關聯後剩餘交易所數量: exchangesNum:", exchangesNum); // 測試
    
        // 調用控制器對象的初始化函數
        TC.InitTask();
    }else if(AutoRecoveryState == false && Positions.length !== 0){                    // 未開啓恢復,有持倉
        throw "未開啓自動恢復,檢測到有持倉信息:" + JSON.stringify(Positions);
    }else if(AutoRecoveryState == true && Positions.length == 0 && (istry == false || IsVirtual())){    // 開啓自動恢復, 無持倉信息
        throw "未檢測到持倉信息,沒法恢復!";
    }else{                                                                             // 恢復持倉
        Log("恢復持倉!");
        if(isCreateHedgeList == true){
            HedgeTable = CreateHedgeList(begin, end, size, step, amountOfPoint, symbolA, symbolB);
        }

        // 輸出對衝控制表,並檢查。
        Log("對衝控制表", HedgeTable);
        if(HedgeTable == "(this_week&quarter)30:15:1"){
            Log("注意!對衝控制表 當前爲 默認值!可能引發虧損。");
        }
    
        // 建立控制器對象
        TC = TasksController();
    
        var exchangesNum = 0;
        var e_index = 0;
        for(var i = 0; i < exchanges.length; i++){ // 遍歷交易所對象數組
            exchangesNum++;
        }

        var arr = HedgeTable.split('(');

        _.each(arr, function(item) {
            if (item != '') {
                var tmp = item.split(')');
                var pair = tmp[0].replace('(', '').split('&');
                // Log("pair:", pair, "tmp:", tmp); // 測試 顯示 處理後的 控制表字符串
                TC.AddTask(exchanges[e_index], pair[0], pair[1], tmp[1]);
                if(For_USD){                                                     // 設置匯率
                    OK_Rate = 1;
                    exchanges[e_index].SetRate(1);
                    Log("使用的價格爲美圓計價,匯率:", OK_Rate, ",請注意 」對衝 控制表「 是否爲美圓差價#FF0000"); // 幣種提示
                }else{
                    OK_Rate = exchanges[e_index].GetUSDCNY();
                    exchanges[e_index].SetRate(OK_Rate);
                    Log("使用的價格爲RMB計價,匯率:", OK_Rate, ",請注意 」對衝 控制表「 是否爲RMB差價#FF0000");  // 幣種提示
                }
                if(--exchangesNum < 0){
                    throw "添加的交易所 和 對衝控制表 不匹配!交易所個數:" + exchanges.length + " arr:" + arr;
                }else if(exchangesNum !== 0){
                    e_index++;
                }
            }
        });
    
        Log("交易所對象最大索引 e_index:", e_index, "關聯後剩餘交易所數量: exchangesNum:", exchangesNum); // 測試
    
        // 調用控制器對象的初始化函數
        TC.InitTask();
        var PreTC = _G("TC");
        for(var index = 0; index < TC.tasks.length ; index++){
            for(var key in TC.tasks[index]){
                if(key == "e" || key == "Adepth" || key == "Bdepth" || key == "P"){
                    continue;
                }
                TC.tasks[index][key] = PreTC.tasks[index][key];
            }
        }
    }

    while(true){
        var nowTime = new Date().getTime();
        TC.DealTask(nowTime);
        Sleep(Interval);
        var endTime = new Date().getTime();
        if(times < 100000){
            SumUseTime += (endTime - nowTime);
            times++;
        }else{
            SumUseTime = 0;
            times = 0;
            SumUseTime += (endTime - nowTime);
            times++;
        }
        sumTimes++;
        var avgUseTime = _N(SumUseTime / times);
        StrLoopTime = (endTime - nowTime) + " ms " + "平均耗時:" + avgUseTime + "循環次數:" + sumTimes;
    }
}

function onexit(){
    Log("退出策略,自動保存狀態。");
    _G("TC", TC);
}
複製代碼

原文:quant.la/Article/Vie…測試

相關文章
相關標籤/搜索