跨期布林对冲策略研究-基于BotVS 量化平台

(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**声明:

该策略为平台用户 小小梦 分享,主要用于 BotVS 平台策略编写学习, 实盘虽可用,风险自担! 欢迎大家讨论,多交流,多学习,多提升! 作者本人其实不喜欢闭门造车,进步就要互相学习!
上个回测:

跨期布林对冲策略研究-基于BotVS 量化平台_第1张图片

因为频率挺高的,回测滑点等因素影响较小,所以回测仅供参考,策略应该可以进一步优化,小心参数撞车!
原创文章,BotVS版权所有。

你可能感兴趣的:(跨期布林对冲策略研究-基于BotVS 量化平台)