跨期布林對衝策略研究-基於發明者量化平臺
(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** 宣告:
該策略為平臺使用者 小小夢 分享,主要用於發明者量化平臺策略編寫學習, 實盤雖可用,風險自擔! 歡迎大家討論,多交流,多學習,多提升! 作者本人其實不喜歡閉門造車,進步就要互相學習!
上個回測:
因為頻率挺高的,回測滑點等因素影響較小,所以回測僅供參考,策略應該可以進一步優化,小心引數撞車!原創文章,發明者量化版權所有。