1. 程式人生 > >跨期布林對衝策略研究-基於發明者量化平臺

跨期布林對衝策略研究-基於發明者量化平臺

(Boll)指標是股市技術分析的常用工具之一,通過計算股價的“標準差”,再求股價的“信賴區間”。該指標在圖形上畫出三條線,其中上下兩條線可以分別看成是股價的壓力線和支撐線,而在兩條線之間還有一條股價平均線,布林線指標的引數最好設為20。一般來說,股價會執行在壓力線和支撐線所形成的通道中。

與MACD、RSI、KDJ等指標一樣,BOLL指標也是股票市場最實用的技術分析參考指標。

 

但是這些指標也都是對於歷史資料的統計計算形成的,客觀的講是不能絕對預測未來標的物的走勢行情的。根據歷史的行情資料,計算出的指標,可以作為參考、輔助決策,最終會發現一切都是圍繞著概率。

 

那麼 BOLL 指標的實際意義是 標的物價格的 標準差,期貨的不同合約之間都有一定的價差,通常情況下 行情平穩的時候價差是保持一定趨勢緩慢變動的,遇到“不理性”的訂單的時候往往會出現短暫的波動,試想如何抓住這些“不理性”的訂單呢?

 

我們考慮構建一個 《跨期布林對衝策略》 ,從底層擼程式碼、造車輪 太慢了, 使用 BotVS 分分鐘實現你的各種交易思路。

 

實現這個策略,我們選擇標的物為 : OKEX 的 BTC 合約, 稍後我也會寫出 標的物 為: 商品期貨 合約的該策略 (為什麼用BTC 合約? 因為好實現,方便快速寫出來 測試!標的物只是一方面,關鍵是思路 、策略!)

 

跨期布林對衝策略

var P_isSetOneKLine = true;
var isShowChart = true;
var retrySlidePrice = 2;

// 全域性變數
var KPeriod = [PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_D1][P_KPeriod];
var DiffData = {       // 差價K線資料
    plusKLine : [],      // 差價K線陣列
    minusKLine : [],
    KPeriod : KPeriod,    // 差價K線週期
};

var distance = 0;                     // 距離
var IDLE = 0;
var PLUS = 1;
var MINUS = 2;
var State = IDLE;                     // 策略 執行狀態狀態
var perState = IDLE;

var OPEN = 3;                         // 用於區分  是那種操作  在 BuyA_SellB 、 SellA_BuyB  函式中
var COVER = 4;
var isTradeOnThisBar = false;         // 是否在當前Bar 交易了   廢棄
var isCoverOnthisBar = false;         //                     廢棄
var upRatio = 0;
var downRatio = 0;

var P = null;                         // 交易控制物件
var ManagerData = {                   // 控制物件
    initAccount : null,               // 初始賬戶 資訊
    Account : null,                   // 當前賬戶 資訊
    perAccount : null,
    MaxUsedRatio : MaxUsedRatio,      // 最大 保證金使用率
    MaxUsedMargin : 0,                // 每次 平倉後 重新計算的 最大保證金使用量
    APosition : null,                 // 多頭 倉位資訊
    BPosition : null,                 // 空頭 倉位資訊 
    floatProfit : 0,                  // 浮動盈虧 
    CoverProfit : 0,                  // 平倉盈虧
    MarginLevel : MarginLevel,        // 槓桿數
    Margin : 0,                       // 當前保證金使用

    // 行情資料
    DepthA : null,                    // A 合約的 深度資料
    DepthB : null,                    // B 合約的 深度資料

    // 錯誤資訊
    ErrorStr : "",                    // 錯誤資訊字串
    ErrorTimeStamp : 0,               // 錯誤資訊 時間戳
}

var DiffDataOneKLine = {              // K線結構
    KLine : [],                       // K線陣列 ,整理後的差價
    KPeriod : KPeriod,                // K線週期
}
var test = {                          // 測試 模式 使用的結構
    amount : 4,
    ProfitDiff : 0,
    ID : 0,
    OpenDiff : 0,
};
var perRecordsTime = 0;               // 合成的 K線 上一個bar 的時間戳

var PeriodBeginTime = 0;              // 週期起始時間

function UpdateDiffData(DepthA, DepthB, RecordsA, RecordsB){ // 處理差價K線資料的更新
    var plusDiff = DepthA.Bids[0].Price - DepthB.Asks[0].Price;
    var minusDiff = DepthA.Asks[0].Price - DepthB.Bids[0].Price;
    if(P_isSetOneKLine){
        var Diff = _N((plusDiff + minusDiff) / 2 , 4);
    }
    if(RecordsA[RecordsA.length - 1].Time == RecordsB[RecordsB.length - 1].Time && RecordsB[RecordsB.length - 1].Time !== PeriodBeginTime){
        if(!P_isSetOneKLine){
            // 新K線週期出現
            var plusBar = {
                Open : plusDiff,
                Close : plusDiff,
                High : plusDiff,
                Low : plusDiff,
                Time : 0,
            };

            var minusBar = {
                Open : minusDiff,
                Close : minusDiff,
                High : minusDiff,
                Low : minusDiff,
                Time : 0,
            };

            PeriodBeginTime = RecordsB[RecordsB.length - 1].Time;
            plusBar.Time = PeriodBeginTime;
            minusBar.Time = PeriodBeginTime;
            DiffData.plusKLine.push(plusBar);
            DiffData.minusKLine.push(minusBar);
        }else{
            var Bar = {
                Open : Diff,
                Close : Diff,
                High : Diff,
                Low : Diff,
                Time : 0,
            };
            PeriodBeginTime = RecordsB[RecordsB.length - 1].Time;
            Bar.Time = PeriodBeginTime;
            DiffDataOneKLine.KLine.push(Bar);
        }
    }else{
        if(!P_isSetOneKLine){
            if(plusDiff > DiffData.plusKLine[DiffData.plusKLine.length - 1].High){
                DiffData.plusKLine[DiffData.plusKLine.length - 1].High = plusDiff;
            }else if(plusDiff < DiffData.plusKLine[DiffData.plusKLine.length - 1].Low){
                DiffData.plusKLine[DiffData.plusKLine.length - 1].Low = plusDiff;
            }
            DiffData.plusKLine[DiffData.plusKLine.length - 1].Close = plusDiff;

            if(minusDiff > DiffData.minusKLine[DiffData.minusKLine.length - 1].High){
                DiffData.minusKLine[DiffData.minusKLine.length - 1].High = minusDiff;
            }else if(minusDiff < DiffData.minusKLine[DiffData.minusKLine.length - 1].Low){
                DiffData.minusKLine[DiffData.minusKLine.length - 1].Low = minusDiff;
            }
            DiffData.minusKLine[DiffData.minusKLine.length - 1].Close = minusDiff;
        }else{
            if(Diff > DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].High){
                DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].High = Diff;
            }else if(Diff < DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Low){
                DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Low = Diff;
            }
            DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close = Diff;
        }
    }

    if(!P_isSetOneKLine){
        if(DiffData.plusKLine.length > 100){
            DiffData.plusKLine.shift();
        }

        if(DiffData.minusKLine.length > 100){
            DiffData.minusKLine.shift();
        }
    }else{
        if(DiffDataOneKLine.KLine.length > 100){
            DiffDataOneKLine.KLine.shift();
        }
    }
}

function UseAPI(fun, symbol, param){
    exchange.SetContractType(symbol);
    if(typeof(param) == "undefined"){
        return fun();
    }else{
        return fun(param);
    }
}

function GetPositions(){
    var positions = _C(exchange.GetPosition);
    for(var i = 0; i < positions.length; i++){
        if(positions[i].ContractType == P_SymbolA){
            ManagerData.APosition = positions[i];
        }else if(positions[i].ContractType == P_SymbolB){
            ManagerData.BPosition = positions[i];
        }else if(i == 2){
            throw "error:" + JSON.stringify(positions);
        }
    }
    if(positions.length == 0){
        ManagerData.APosition = null;
        ManagerData.BPosition = null;
    }
    return positions;
}

function SellA_BuyB(DiffPrice, Action){
    // GetBestAmount
    var Piece = P_piece;
    if(Action == OPEN){
        // 檢查 是否超出 保證金
        var MarginAndAmount_A = CalcPieceEqualAmount(ManagerData.DepthA.Bids[0].Price, Piece);
        var MarginAndAmount_B = CalcPieceEqualAmount(ManagerData.DepthB.Asks[0].Price, Piece);
        if(MarginAndAmount_A.NeedMargin + MarginAndAmount_B.NeedMargin + ManagerData.Margin > ManagerData.MaxUsedMargin){
            ManagerData.ErrorStr = "超過最大可用保證金:" + ManagerData.MaxUsedMargin;
            ManagerData.ErrorTimeStamp = new Date().getTime();
            return false;
        }
        // 交易
        _C(exchange.SetContractType, P_SymbolA);
        var infoA = P.OpenShort(P_SymbolA, Piece, ManagerData.DepthA.Bids[0].Price);
        if(infoA.amount == 0){
            return false;
        }
        _C(exchange.SetContractType, P_SymbolB);
        var dealAmount = infoA.amount;
        var infoB = P.OpenLong(P_SymbolB, dealAmount, ManagerData.DepthB.Asks[0].Price);
        var slidePrice = retrySlidePrice;  
        while((dealAmount -= infoB.amount) > 0){
            infoB = P.OpenLong(P_SymbolB, dealAmount, ManagerData.DepthB.Asks[0].Price + slidePrice);
            slidePrice += retrySlidePrice;
            LogStatus("while(), P_SymbolB:", P_SymbolB, infoB, dealAmount, slidePrice);
        }

        // 更新資料
        ManagerData.perAccount = ManagerData.Account;
        ManagerData.Account = _C(exchange.GetAccount);
        ManagerData.Margin += (ManagerData.perAccount.Stocks - ManagerData.Account.Stocks);
        Log("持倉資訊:", GetPositions(), "本次使用保證金:", (ManagerData.perAccount.Stocks - ManagerData.Account.Stocks));
        perState = State;
        State = PLUS;
    }else if(Action == COVER){
        // 全平
        _C(exchange.SetContractType, P_SymbolA);
        var infoA = P.Cover(P_SymbolA, ManagerData.DepthA.Bids[0].Price, Math.min(ManagerData.APosition.Amount, Math.max(_N(ManagerData.APosition.Amount * CoverRatio, 0), minCoverAmount)), PD_LONG);
        if(infoA == 0){
            return false;
        }
        _C(exchange.SetContractType, P_SymbolB);
        var dealAmount = infoA;
        var infoB = P.Cover(P_SymbolB, ManagerData.DepthB.Asks[0].Price, dealAmount, PD_SHORT);
        var slidePrice = retrySlidePrice;
        while((dealAmount -= infoB) > 0){
            infoB = P.Cover(P_SymbolB, ManagerData.DepthB.Asks[0].Price + slidePrice, dealAmount, PD_SHORT);  
            slidePrice += retrySlidePrice;
            LogStatus("while(), P_SymbolB:", P_SymbolB, infoB, dealAmount, slidePrice);
        }
        Log("持倉資訊:", GetPositions());
        if(ManagerData.APosition == null && ManagerData.BPosition == null){
            perState = State;
            State = IDLE;
            upRatio = 0;
            downRatio = 0;
            Log("完全平倉!#FF0000");
            Log("更新最大保證金使用量:", DealManagerData());
            LogProfit(ManagerData.Account.Stocks - ManagerData.initAccount.Stocks);  // ceshi 
        }else{
            ManagerData.perAccount = ManagerData.Account;
            ManagerData.Account = _C(exchange.GetAccount); 
            Log("部分平倉:", infoA);
        }
    }
    return true;
}

function BuyA_SellB(DiffPrice, Action){
    // GetBestAmount
    var Piece = P_piece;
    if(Action == OPEN){
        // 檢查 是否超出 保證金
        var MarginAndAmount_A = CalcPieceEqualAmount(ManagerData.DepthA.Asks[0].Price, Piece);
        var MarginAndAmount_B = CalcPieceEqualAmount(ManagerData.DepthB.Bids[0].Price, Piece);
        if(MarginAndAmount_A.NeedMargin + MarginAndAmount_B.NeedMargin + ManagerData.Margin > ManagerData.MaxUsedMargin){
            ManagerData.ErrorStr = "超過最大可用保證金:" + ManagerData.MaxUsedMargin;
            ManagerData.ErrorTimeStamp = new Date().getTime();
            return false;
        }
        // 交易
        _C(exchange.SetContractType, P_SymbolA);
        var infoA = P.OpenLong(P_SymbolA, Piece, ManagerData.DepthA.Asks[0].Price);
        if(infoA.amount == 0){
            return false;
        }
        _C(exchange.SetContractType, P_SymbolB);
        var dealAmount = infoA.amount;
        var infoB = P.OpenShort(P_SymbolB, dealAmount, ManagerData.DepthB.Bids[0].Price);
        var slidePrice = retrySlidePrice; 
        while((dealAmount -= infoB.amount) > 0){
            infoB = P.OpenShort(P_SymbolB, dealAmount, ManagerData.DepthB.Bids[0].Price - slidePrice);
            slidePrice += retrySlidePrice;
            LogStatus("while(), P_SymbolB:", P_SymbolB, infoB, dealAmount, slidePrice);
        }

        // 更新資料
        ManagerData.perAccount = ManagerData.Account;
        ManagerData.Account = _C(exchange.GetAccount);
        ManagerData.Margin += (ManagerData.perAccount.Stocks - ManagerData.Account.Stocks);
        Log("持倉資訊:", GetPositions(), "本次使用保證金:", (ManagerData.perAccount.Stocks - ManagerData.Account.Stocks));
        perState = State;
        State = MINUS;
    }else if(Action == COVER){
        // 全平
        _C(exchange.SetContractType, P_SymbolA);
        var infoA = P.Cover(P_SymbolA, ManagerData.DepthA.Asks[0].Price, Math.min(ManagerData.APosition.Amount, Math.max(_N(ManagerData.APosition.Amount * CoverRatio, 0), minCoverAmount)), PD_SHORT);
        if(infoA == 0){
            return false;
        }
        _C(exchange.SetContractType, P_SymbolB);
        var dealAmount = infoA;
        var infoB = P.Cover(P_SymbolB, ManagerData.DepthB.Bids[0].Price, dealAmount, PD_LONG);
        var slidePrice = retrySlidePrice;
        while((dealAmount -= infoB) > 0){
            infoB = P.Cover(P_SymbolB, ManagerData.DepthB.Bids[0].Price - slidePrice, dealAmount, PD_LONG);
            slidePrice += retrySlidePrice;
            LogStatus("while(), P_SymbolB:", P_SymbolB, infoB, dealAmount, slidePrice);
        }
        Log("持倉資訊:", GetPositions());
        if(ManagerData.APosition == null && ManagerData.BPosition == null){
            perState = State;
            State = IDLE;
            upRatio = 0;    // 重置
            downRatio = 0;  // 重置
            Log("完全平倉!#FF0000");
            Log("更新最大保證金使用量:", DealManagerData());
            LogProfit(ManagerData.Account.Stocks - ManagerData.initAccount.Stocks);  // ceshi 
        }else{
            ManagerData.perAccount = ManagerData.Account;
            ManagerData.Account = _C(exchange.GetAccount); 
            Log("部分平倉:", infoA);
        }
    }
    return true;
}

function DealManagerData(){                                                       // 處理 ManagerData
    ManagerData.perAccount = ManagerData.Account;
    ManagerData.Account = _C(exchange.GetAccount);                                // 獲取 平倉後的 賬戶資訊
    var Position = _C(exchange.GetPosition);                                      // 獲取 平倉後的持倉資訊
    if(Position.length == 0){                                                     // 如果 平倉完成 ,沒有持倉了 重置 ManagerData 的 持倉資訊屬性
        ManagerData.APosition = null;
        ManagerData.BPosition = null;
        ManagerData.Margin = 0;                                                   // 重置 當前使用的保證金 為 0
    }else{                                                                        // 如果有持倉 報錯,列印持倉
        Log("平倉後還有持倉!", Position, "#FF0000");
        throw "error!";
    }
    ManagerData.MaxUsedMargin = _N(ManagerData.Account.Stocks * ManagerData.MaxUsedRatio, 2);         // 計算下一輪 最大的 保證金使用量
    return ManagerData.MaxUsedMargin;
}

function CalcPieceEqualAmount(Price, Piece){
   return {
            EqualAmount : (100 / Price * Piece), 
            NeedMargin : (100 / Price * Piece) / ManagerData.MarginLevel,
        }
}

function Loop(nowTime){       // 主迴圈
    var DepthA = UseAPI(exchange.GetDepth, P_SymbolA);
    var DepthB = UseAPI(exchange.GetDepth, P_SymbolB);

    var RecordsA = UseAPI(exchange.GetRecords, P_SymbolA, KPeriod);
    var RecordsB = UseAPI(exchange.GetRecords, P_SymbolB, KPeriod);

    // 過濾資料
    if(!RecordsA || !RecordsB || !DepthA || !DepthB || DepthA.Bids.length == 0 || DepthA.Asks.length == 0 || 
            DepthB.Bids.length == 0 || DepthB.Asks.length == 0 || RecordsA.length == 0 || RecordsB.length == 0){
        return;
    }

    ManagerData.DepthA = DepthA;
    ManagerData.DepthB = DepthB;

    // 更新 差價K線資料
    UpdateDiffData(DepthA, DepthB, RecordsA, RecordsB);

    if(isShowChart == true){
        if(!P_isSetOneKLine){
            $.PlotRecords(DiffData.plusKLine, 'plus');
        }else{
            $.PlotRecords(DiffDataOneKLine.KLine, 'KLine');
        }
    }

    // BOLL
    if(P_isSetOneKLine && DiffDataOneKLine.KLine.length > 20){
        var boll = TA.BOLL(DiffDataOneKLine.KLine);
        var up = boll[0];
        var mid = boll[1];
        var down = boll[2];

        if(perRecordsTime !== DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time){
            if(isShowChart == true){
                $.PlotLine("up", up[up.length - 2], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 2].Time);
                $.PlotLine("mid", mid[mid.length - 2], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 2].Time);
                $.PlotLine("down", down[down.length - 2], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 2].Time);

                $.PlotLine("up", up[up.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);
                $.PlotLine("mid", mid[mid.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);
                $.PlotLine("down", down[down.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);

                // $.PlotHLine(up[up.length - 1], 'UpTrack', "red");
                // $.PlotHLine(down[down.length - 1], 'DownTrack', "green");
            }
            perRecordsTime = DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time;
            isTradeOnThisBar = false;       // 重置 是否當前Bar 交易的標記為 false
            isCoverOnthisBar = false;
        }else{
            if(isShowChart == true){
                $.PlotLine("up", up[up.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);
                $.PlotLine("mid", mid[mid.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);
                $.PlotLine("down", down[down.length - 1], DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time);

                // $.PlotHLine(up[up.length - 1], 'UpTrack', "red");
                // $.PlotHLine(down[down.length - 1], 'DownTrack', "green");
            }
        }


        // 突破判斷
        // 計算加倉距離
        distance = _N((up[up.length - 1] - down[down.length - 1]) * 0.5, 0);

        if(distance * 2 < 20 && (upRatio == 0 && downRatio == 0)){  // 計算安全間距
            return;
        }

        if(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close > up[up.length - 1]){
            if((State == IDLE || State == PLUS) && (DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close > (up[up.length - 1] + upRatio * distance)) /*&& isTradeOnThisBar == false*/){  // Open PLUS
                if(P_isSetTest){
                    // 模擬
                    $.PlotFlag(nowTime, 'plus', 'P', 'flag', 'red');
                    State = PLUS;
                    exchange.Log(LOG_TYPE_SELL, test.ID++, DepthA.Bids[0].Price, test.amount, "Open:" + P_SymbolA);
                    exchange.Log(LOG_TYPE_BUY, test.ID++, DepthB.Asks[0].Price, test.amount, "Open:" + P_SymbolB);
                    test.OpenDiff = DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close;
                }else{
                    var info = SellA_BuyB(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close, OPEN);
                    if(info == false){
                        return;
                    }
                    $.PlotFlag(nowTime, 'plus', 'P', 'flag', 'red');
                    isTradeOnThisBar = true;
                    upRatio += Add_lv;   
                }
            }else if(State == MINUS /*&& isCoverOnthisBar == false*/){  // Cover MINUS
                if(P_isSetTest){
                    // 模擬
                    $.PlotFlag(nowTime, 'Cover_Minus', 'CM', 'circlepin', 'blue');
                    State = IDLE;
                    exchange.Log(LOG_TYPE_SELL, test.ID++, DepthA.Bids[0].Price, test.amount, "Cover:" + P_SymbolA);
                    exchange.Log(LOG_TYPE_BUY, test.ID++, DepthB.Asks[0].Price, test.amount, "Cover:" + P_SymbolB);
                    test.ProfitDiff += DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close - test.OpenDiff;
                    Log("本次盈虧:" + (DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close - test.OpenDiff));
                    LogProfit(test.ProfitDiff);
                }else{
                    // SellA_BuyB    
                    var info = SellA_BuyB(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close, COVER);
                    if(info == false){
                        Log("MINUS 平倉失敗!");
                        return;
                    }
                    $.PlotFlag(nowTime, 'Cover_Minus', 'CM', 'circlepin', 'blue');
                    isCoverOnthisBar = true;
                }
            }
        }else if(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close < down[down.length - 1]){
            if((State == IDLE || State == MINUS) && (DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close < (down[down.length - 1] - downRatio * distance)) /*&& isTradeOnThisBar == false*/){
                if(P_isSetTest){
                    // 模擬
                    $.PlotFlag(nowTime, 'minus', 'M', 'circlepin', 'green'); // DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Time
                    State = MINUS;
                    exchange.Log(LOG_TYPE_BUY, test.ID++, DepthA.Asks[0].Price, test.amount, "Open:" + P_SymbolA);
                    exchange.Log(LOG_TYPE_SELL, test.ID++, DepthB.Bids[0].Price, test.amount, "Open:" + P_SymbolB);
                    test.OpenDiff = DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close;
                }else{
                    // BuyA_SellB
                    var info = BuyA_SellB(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close, OPEN);
                    if(info == false){
                        return;
                    }
                    $.PlotFlag(nowTime, 'minus', 'M', 'circlepin', 'green');
                    isTradeOnThisBar = true;
                    downRatio += Add_lv; 
                }
            }else if(State == PLUS /*&& isCoverOnthisBar == false*/){
                if(P_isSetTest){
                    // 模擬
                    $.PlotFlag(nowTime, 'Cover_Plus', 'CP', 'flag', 'blue');
                    State = IDLE;
                    exchange.Log(LOG_TYPE_BUY, test.ID++, DepthA.Asks[0].Price, test.amount, "Cover:" + P_SymbolA);
                    exchange.Log(LOG_TYPE_SELL, test.ID++, DepthB.Bids[0].Price, test.amount, "Cover:" + P_SymbolB);
                    test.ProfitDiff += test.OpenDiff - DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close;
                    Log("本次盈虧:" + (test.OpenDiff - DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close));
                    LogProfit(test.ProfitDiff);
                }else{
                    // BuyA_SellB
                    var info = BuyA_SellB(DiffDataOneKLine.KLine[DiffDataOneKLine.KLine.length - 1].Close, COVER);
                    if(info == false){
                        Log("PLUS 平倉失敗!");
                        return;
                    }
                    $.PlotFlag(nowTime, 'Cover_Plus', 'CP', 'flag', 'blue');
                    isCoverOnthisBar = true;
                }
            }
        }
    }

}

function main(){
    // 初始化工作
    if(isLogReset){
        LogReset(1);
    }
    Log("最大保證金使用量:", DealManagerData());
    ManagerData.initAccount = ManagerData.Account;
    ManagerData.perAccount = ManagerData.Account;
    exchange.SetMarginLevel(ManagerData.MarginLevel); // 初始設定槓桿

    // 設定禁用匯率
    exchange.SetRate(1);
    Log("已禁用匯率,顯示價格為美元價格!");

    // 建立交易控制物件
    P = $.NewPositionManager(exchange);

    //主要迴圈
    while(true){
        var nowTime = new Date().getTime();
        Loop(nowTime);
        LogStatus("時間:", _D(nowTime), '\n', P_isSetTest ? JSON.stringify(test) : "Margin: " + ManagerData.Margin, '\n', ManagerData.ErrorStr, _D(ManagerData.ErrorTimeStamp), '\n', 
            ManagerData.initAccount, '\n', ManagerData.Account, '\n', ManagerData.APosition, '\n', ManagerData.BPosition, '\n', "upRatio:" + upRatio, '\n', "downRatio:" + downRatio, "distance:" + distance);
        Sleep(P_Interval);
    }
}

 

程式語言使用的是 JavaScript , 引用了 “ 模板-畫線類庫” 和 “ OKCoin期貨跨期對衝策略(模板測試使用)” 兩個模板, 策略公開地址: https://www.botvs.com/strategy/43049** 宣告:

 

該策略為平臺使用者 小小夢 分享,主要用於發明者量化平臺策略編寫學習, 實盤雖可用,風險自擔! 歡迎大家討論,多交流,多學習,多提升! 作者本人其實不喜歡閉門造車,進步就要互相學習!

 

上個回測:

回測

因為頻率挺高的,回測滑點等因素影響較小,所以回測僅供參考,策略應該可以進一步優化,小心引數撞車!原創文章,發明者量化版權所有。