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 = "  " + 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 = "  " + 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 = "  "; 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); } 複製代碼