戦略原理
戦略原理は非常にシンプルで,OKEXの契約跨期ヘッジは,ポジション制御の設計面では,格子ヘッジとして設計されている. 戦略は2つの契約,A契約,B契約を定義する. 契約は異なる契約コードを設定してヘッジすることができます. 例えば,Aを四半期契約,Bを週間の契約 (Aを近期契約,Bを長期契約,その他の定義は逆) と設定します. ヘッジ操作は,空白A契約 (四半期) や多重B契約 (商品先物における跨期配当の空白長期契約,多期近期契約,正套) に分かれている. 多A契約,空白B契約 (類似商品先物における空白近期,どのくらいの長期,反セット)
コード言語 策略コードはC++言語を使用し,高速な性能優位性を持っています.
ビジネス・ドライブ: OKEX Websocket を採用した市場駆動インターフェイスが取引所への市場推移を受け取り,最新の市場がよりタイムリーに入手され,市場データは少ないデータを使用し,リアルタイムのティックデータを使用します. 市場応答速度は大きく向上した. ティックデータについては,策略がK線生成器を特別に構築し,取得したティックデータの計算後の契約差値にK線合成を行う. 戦略的なヘッジ操作のオープン・オフ・ポジションは,このK線生成器クラスオブジェクトによって生成されたデータによって動いている.
ポジション制御 ポジションコントロールは",ポフィナッチ"のような数列のヘッジポジション比率を用いて制御する. 差額が大きくなるほど,利息ヘッジの数が相対的に増加し,ポジションを分散させ,小差額波動の小ポジションを把握し,大きな差額波動のポジションを適切に拡大する.
平仓:停止損失停止
固定停止価格,停止損失価格.
持股差が
市場投入,市場離脱 サイクルデザイン パラメータ NPeriod 制御された周期は,戦略の開場平衡に一定の動的制御を行います.
ポジションバランスシステム,オーダー検出システム 戦略には定期的な検査とバランスシステムがあります. 注文検知システム.
戦略の拡大
策略コード設計は,低
戦略図 戦略は,関連取引情報をマークするK線グラフを自動的に生成します.
/*backtest start: 2019-07-22 00:00:00 end: 2019-08-21 00:00:00 period: 1m exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD","stocks":0.1,"fee":[0.02,0.05]}] args: [["InstrumentB","quarter"],["NPeriod",200],["LeavePeriod",100],["AddMax",3],["StopLoss",20],["StopWin",50],["OpenAmount",2]] */ enum State { STATE_NA, STATE_IDLE, STATE_HOLD_LONG, STATE_HOLD_SHORT, }; string replace(string s, const string from, const string& to) { if(!from.empty()) for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size()) s.replace(pos, from.size(), to); return s; } class BarFeeder { public: BarFeeder(int period) : _period(period) { _rs.Valid = true; } void feed(double price, Chart *c=nullptr, int chartIdx=0) { uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000; bool newBar = false; if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) { Record r; r.Time = epoch; r.Open = r.High = r.Low = r.Close = price; _rs.push_back(r); if (_rs.size() > 2000) { _rs.erase(_rs.begin()); } newBar = true; } else { Record &r = _rs[_rs.size() - 1]; r.High = max(r.High, price); r.Low = min(r.Low, price); r.Close = price; } auto bar = _rs[_rs.size()-1]; json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.Close}; if (c != nullptr) { if (newBar) { c->add(chartIdx, point); c->reset(1000); } else { c->add(chartIdx, point, -1); } } } Records & get() { return _rs; } private: int _period; Records _rs; }; class Hedge { public: Hedge() { _isCover = true; _needCheckOrder = true; _st = STATE_NA; for (int i = 0; i < AddMax + 1; i++) { if (_addArr.size() < 2) { _addArr.push_back((i+1)*OpenAmount); } _addArr.push_back(_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); } _cfgStr = R"EOF( [{ "extension": { "layout": "single", "col": 6, "height": "500px"}, "rangeSelector": {"enabled": false}, "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"}, "plotOptions": {"candlestick": {"color": "#d75442", "upColor": "#6ba583"}}, "chart":{"type":"line"}, "title":{"text":"Spread Long"}, "xAxis":{"title":{"text":"Date"}}, "series":[ {"type":"candlestick", "name":"Long Spread","data":[], "id":"dataseriesA"}, {"type":"flags","data":[], "onSeries": "dataseriesA"} ] }, { "extension": { "layout": "single", "col": 6, "height": "500px"}, "rangeSelector": {"enabled": false}, "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"}, "plotOptions": {"candlestick": {"color": "#d75442", "upColor": "#6ba583"}}, "chart":{"type":"line"}, "title":{"text":"Spread Short"}, "xAxis":{"title":{"text":"Date"}}, "series":[ {"type":"candlestick", "name":"Long Spread","data":[], "id":"dataseriesA"}, {"type":"flags","data":[], "onSeries": "dataseriesA"} ] } ] )EOF"; _c.update(_cfgStr); _c.reset(); }; State getState(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB) { if (!_needCheckOrder && _st != STATE_NA) { return _st; } //Log("sync orders"); auto orders = exchange.GetOrders(); if (!orders.Valid) { return STATE_NA; } if (orders.size() > 0) { for (auto &order : orders) { exchange.CancelOrder(order.Id); } return STATE_NA; } Sleep(500); //Log("sync positions"); auto positions = exchange.GetPosition(); if (!positions.Valid) { return STATE_NA; } // cache orders and positions; _needCheckOrder = false; if (positions.size() == 0) { //Log("Position is empty"); return STATE_IDLE; } State st[2] = {STATE_IDLE, STATE_IDLE}; double holdAmount[2] = {0, 0}; double holdPrice[2] = {}; for (auto &pos : positions) { int idx = -1; if (pos.ContractType == symbolA) { idx = 0; } else if (pos.ContractType == symbolB) { idx = 1; } if (idx >= 0) { holdPrice[idx] = pos.Price; holdAmount[idx] += pos.Amount; st[idx] = pos.Type == PD_LONG || pos.Type == PD_LONG_YD ? STATE_HOLD_LONG : STATE_HOLD_SHORT; } } if (holdAmount[0] > holdAmount[1]) { st[1] = STATE_IDLE; } else if (holdAmount[0] < holdAmount[1]) { st[0] = STATE_IDLE; } if (st[0] != STATE_IDLE && st[1] != STATE_IDLE) { // update _holdPrice = _N(holdPrice[1] - holdPrice[0], 4); _holdAmount = holdAmount[0]; return st[0]; } else if (st[0] == STATE_IDLE && st[1] == STATE_IDLE) { return STATE_IDLE; } else { double amount = abs(holdAmount[0] - holdAmount[1]); auto idx_fat = st[0] == STATE_IDLE ? 1 : 0; if (_isCover) { exchange.SetContractType(st[0] == STATE_IDLE ? symbolB : symbolA); if (st[idx_fat] == STATE_HOLD_LONG) { exchange.SetDirection("closebuy"); exchange.Sell((st[0] == STATE_IDLE ? depthB.Bids[0].Price: depthA.Bids[0].Price)-SlidePrice, amount); } else { exchange.SetDirection("closesell"); exchange.Buy((st[0] == STATE_IDLE ? depthB.Asks[0].Price : depthA.Asks[0].Price)+SlidePrice, amount); } } else { exchange.SetContractType(st[0] == STATE_IDLE ? symbolA : symbolB); if (st[idx_fat] == STATE_HOLD_LONG) { exchange.SetDirection("sell"); exchange.Sell((st[0] == STATE_IDLE ? depthA.Bids[0].Price : depthB.Bids[0].Price)-SlidePrice, amount); } else { exchange.SetDirection("buy"); exchange.Buy((st[0] == STATE_IDLE ? depthA.Asks[0].Price : depthB.Asks[0].Price)+SlidePrice, amount); } } _needCheckOrder = true; return STATE_NA; } Log(positions); Panic("WTF"); } bool Loop(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB, string extra="") { _loopCount++; auto diffLong = _N(depthB.Bids[0].Price - depthA.Asks[0].Price, 4); auto diffShort = _N(depthB.Asks[0].Price - depthA.Bids[0].Price, 4); _feederA.feed(diffLong, &_c, 0); _feederB.feed(diffShort, &_c, 2); auto barsA = _feederA.get(); auto barsB = _feederB.get(); if (barsA.size() < max(LeavePeriod, NPeriod) + 2) { LogStatus(_D(), "Calc His", barsA.size()); return true; } bool expired = false; auto seconds = Unix(); if (seconds - _lastCache > 600) { _needCheckOrder = true; expired = true; } State st = getState(symbolA, depthA, symbolB, depthB); if (st == STATE_NA) { return true; } if (st == STATE_IDLE) { _holdPrice = 0; } // cache st _st = st; if (expired) { _lastCache = seconds; } if (Unix() - seconds > 5) { Log("skip this tick"); return true; } LogStatus(_D(), "State: ", _state_desc[st], "Hold:", _holdPrice, "Long:", diffLong, "Short:", diffShort, "Loop:", _loopCount, extra); if (st == STATE_IDLE && _isCover) { auto account = exchange.GetAccount(); if (account.Valid) { double profit = _N(exchange.GetName() == "Futures_OKCoin" ? account.Stocks + account.FrozenStocks : account.Balance + account.FrozenBalance, 8); LogProfit(profit, _hedgeCount > 0 ? format("Net: %f @", profit) : ""); } _isCover = false; return true; } auto ratio = abs(diffLong - diffShort); bool condOpenLong = (st == STATE_IDLE || st == STATE_HOLD_LONG) && (diffLong - _countOpen * max(1.0, _holdPrice * 0.1)) > TA.Highest(barsA.High(), NPeriod) && _countOpen < AddMax; bool condOpenShort = (st == STATE_IDLE || st == STATE_HOLD_SHORT) && (diffShort + _countOpen * max(1.0, _holdPrice * 0.1)) < TA.Lowest(barsB.Low(), NPeriod) && _countOpen < AddMax; bool condCoverLong = false; bool condCoverShort = false; bool isLeave = false; bool isStopLoss = false; bool isStopWin = false; if (st == STATE_HOLD_LONG) { auto leavePrice = (diffShort + _countCover + ratio); isLeave = leavePrice < TA.Lowest(barsB.Low(), LeavePeriod); if (!isLeave) { isStopLoss = diffShort - _holdPrice >= StopLoss; if (!isStopLoss) { isStopWin = _holdPrice - diffShort >= StopWin; if (isStopWin) { Log("Stop Win", "HOLD:", _holdPrice, "SHORT:", diffShort); } } else { Log("StopLoss", "HOLD:", _holdPrice, "SHORT:", diffShort); } } else { Log("Leave normally", "LeavePrice:", leavePrice); } condCoverLong = isLeave || isStopLoss || isStopWin; } else if (st == STATE_HOLD_SHORT) { auto leavePrice = (diffLong - _countCover - ratio); isLeave = leavePrice > TA.Highest(barsA.High(), NPeriod); if (!isLeave) { isStopLoss = _holdPrice - diffLong >= StopLoss; if (!isStopLoss) { isStopWin = diffLong - _holdPrice >= StopWin; if (isStopWin) { Log("Stop Win", "HOLD:", _holdPrice, "LONG:", diffLong); } } else { Log("StopLoss", "HOLD:", _holdPrice, "LONG:", diffLong); } } else { Log("Leave normally", "LeavePrice:", leavePrice); } condCoverShort = isLeave || isStopLoss || isStopWin; } string action, color; double opPrice; int chartIdx = 0; if (condOpenLong) { // Must Increase if (_countOpen > 0 && diffLong <= _holdPrice) { return STATE_IDLE; } _isCover = false; _countOpen++; _countCover = 0; _holdPrice = diffLong; auto amount = _addArr[_countOpen]; if (_countOpen > 0) { Log("Add Position Long", _countOpen); } exchange.SetContractType(symbolB); exchange.SetDirection("sell"); exchange.Sell(depthB.Bids[0].Price-SlidePrice, amount); exchange.SetContractType(symbolA); exchange.SetDirection("buy"); exchange.Buy(depthA.Asks[0].Price+SlidePrice, amount); action = "L"; color = "blue"; opPrice = diffLong; chartIdx = 1; } else if (condOpenShort) { // Must Decrease if (_countOpen > 0 && diffShort >= _holdPrice) { return STATE_IDLE; } _isCover = false; _countOpen++; _countCover = 0; _holdPrice = diffShort; auto amount = _addArr[_countOpen]; if (_countOpen > 0) { Log("Add Position Short", _countOpen); } exchange.SetContractType(symbolA); exchange.SetDirection("sell"); exchange.Sell(depthA.Bids[0].Price-SlidePrice, amount); exchange.SetContractType(symbolB); exchange.SetDirection("buy"); exchange.Buy(depthB.Asks[0].Price+SlidePrice, amount); action = "S"; color = "red"; opPrice = diffShort; chartIdx = 3; } else if (condCoverLong) { _isCover = true; _countOpen = 0; _countCover++; _hedgeCount++; if (_countCover > 0) { Log("Cover Position Long", _countCover); } exchange.SetContractType(symbolB); exchange.SetDirection("closesell"); exchange.Buy(depthB.Asks[0].Price+SlidePrice, _holdAmount); exchange.SetContractType(symbolA); exchange.SetDirection("closebuy"); exchange.Sell(depthA.Bids[0].Price-SlidePrice, _holdAmount); action = "CL"; color = "blue"; opPrice = diffShort; chartIdx = 3; } else if (condCoverShort) { _hedgeCount++; _isCover = true; _countOpen = 0; _countCover++; if (_countCover > 0) { Log("Cover Position Short", _countCover); } exchange.SetContractType(symbolA); exchange.SetDirection("closesell"); exchange.Buy(depthA.Asks[0].Price+SlidePrice, _holdAmount); exchange.SetContractType(symbolB); exchange.SetDirection("closebuy"); exchange.Sell(depthB.Bids[0].Price-SlidePrice, _holdAmount); action = "CS"; color = "blue"; opPrice = diffLong; chartIdx = 1; } else { return true; } _needCheckOrder = true; _c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action}, {"text", format("diff: %f", opPrice)}, {"color", color}}); Log(st, "Long:", diffLong, "Short:", diffShort, "Hold:", _holdPrice); return true; } private: vector<double> _addArr; string _state_desc[4] = {"NA", "IDLE", "LONG", "SHORT"}; int _countOpen = 0; int _countCover = 0; int _lastCache = 0; int _hedgeCount = 0; int _loopCount = 0; double _holdPrice = 0; BarFeeder _feederA = BarFeeder(DPeriod); BarFeeder _feederB = BarFeeder(DPeriod); State _st = STATE_NA; string _cfgStr; double _holdAmount = 0; bool _isCover = false; bool _needCheckOrder = true; Chart _c = Chart("{}"); }; inline unsigned char toHex(unsigned char x) { return x > 9 ? x + 55 : x + 48; } std::string urlencode(const std::string& str) { std::string strTemp = ""; size_t length = str.length(); for (size_t i = 0; i < length; i++) { if (isalnum((unsigned char)str[i]) || (str[i] == '-') || (str[i] == '_') || (str[i] == '.') || (str[i] == '~')) strTemp += str[i]; else if (str[i] == ' ') strTemp += "+"; else { strTemp += '%'; strTemp += toHex((unsigned char)str[i] >> 4); strTemp += toHex((unsigned char)str[i] % 16); } } return strTemp; } uint64_t _Time(string &s) { tm t_init; t_init.tm_year = 70; t_init.tm_mon = 0; t_init.tm_mday = 1; t_init.tm_hour = 0; t_init.tm_min = 0; t_init.tm_sec = 0; tm t; int year, month, day, hour, minute, second, ms; sscanf(s.c_str(), "%d-%d-%dT%d:%d:%d.%dZ", &year, &month, &day, &hour, &minute, &second, &ms); t.tm_year = year - 1900; t.tm_mon = month - 1; t.tm_mday = day; t.tm_hour = hour; t.tm_min = minute; t.tm_sec = second; t.tm_isdst = 0; return uint64_t(mktime(&t))*1000+ms-uint64_t(mktime(&t_init))*1000; } void main() { // exchange.IO("base", "https://www.okex.me"); // 测试 if (IsSetProxy) { exchange.SetProxy(Proxy); } LogReset(); LogProfitReset(); SetErrorFilter("ready|timeout|500"); Log("Init OK"); string symbolA = InstrumentA; string symbolB = InstrumentB; Hedge h; if (IsVirtual()) { while (true) { exchange.SetContractType(symbolA); auto depthA = exchange.GetDepth(); if (depthA.Valid) { exchange.SetContractType(symbolB); auto depthB = exchange.GetDepth(); if (depthB.Valid) { h.Loop(symbolA, depthA, symbolB, depthB); } } } return; } if (exchange.GetName() != "Futures_OKCoin") { Panic("only support Futures_OKCoin"); } string realSymbolA = exchange.SetContractType(symbolA)["instrument"]; string realSymbolB = exchange.SetContractType(symbolB)["instrument"]; string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump()); Log("try connect to websocket"); // wss://real.OKEx.com:8443/ws/v3 auto ws = Dial("wss://real.okex.com:8443/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs); // auto ws = Dial("wss://real.okex.me:8443/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs); Log("connect to websocket success"); Depth depthA, depthB; auto fillDepth = [](json &data, Depth &d) { d.Valid = true; d.Asks.clear(); d.Asks.push_back({atof(string(data["asks"][0][0]).c_str()), atof(string(data["asks"][0][1]).c_str())}); d.Bids.clear(); d.Bids.push_back({atof(string(data["bids"][0][0]).c_str()), atof(string(data["bids"][0][1]).c_str())}); }; string timeA; string timeB; while (true) { auto buf = ws.read(); // Log("buf:", buf); // 测试 json obj; try { obj = json::parse(buf); } catch (json::parse_error& e) { Log(buf); Log(e.what()); continue; } if (obj["data"].size() == 0) { continue; } auto data = obj["data"][0]; string ins = data["instrument_id"]; if (ins == realSymbolA) { fillDepth(data, depthA); timeA = data["timestamp"]; } else if (ins == realSymbolB) { fillDepth(data, depthB); timeB = data["timestamp"]; } if (depthA.Valid && depthB.Valid) { auto diffA = uint64_t(UnixNano()/1000000)-_Time(timeA); auto diffB = uint64_t(UnixNano()/1000000)-_Time(timeB); if (diffA > MaxDelay || diffB > MaxDelay) { continue; } h.Loop(symbolA, depthA, symbolB, depthB, format("market delay (ms): %d, %d", diffA, diffB)); } } }
小正想像してみてください 走る際の実盤は 絵のように見えるのですが 走る際の跳びは 繰り返されます Exchange_GetOrders: 429: {"error_message":"Too Many Requests","code":30014,"error_code":"30014","message":"Too Many Requests"} エラーメッセージ: エラーメッセージ Exchange_GetOrders: 400: {"error_message":"Coin type wrong","code":30031,"error_code":"30031","message":"Coin type wrong"} コインタイプが間違っている なぜそうなるのか?
アリン/upload/asset/bb5df259b6a8148b1f65.png 夢大,昨日中午から夜まで走ったのに,注文なし,夜から今まで走ったのに,注文なし,直接実盤に? 0.0
リー・ライソンJSON解析のエラーが表示されています.
エルビス1213/upload/asset/14bc485151de321c0a6a1.jpg しばらく動いていたが,突然問題が発生した.
エルビス1213このエラーを報告するだけで,すぐに数十ページのログ /upload/asset/14bfcf6f9da5f49807e68.jpg
エルビス1213このエラーを /upload/asset/14b9d3530ce1a60bde3ca.jpg で報告していましたが,取引所はOKEXフューチャーを選択しました.
軽い雲/upload/asset/5a8be467dae6c9a52b7d.jpg /upload/asset/5a8be467dae6c9a52b7d.jpg /upload/asset/5a8be467dae6c9a52b7d.jpg /upload/asset/5a8be467dae6c9a52b7d.jpg /upload/asset/5a8be467dae6c9a52b7d.jpg /upload/asset/5a8be467dae6c9a52b7d/ 夢は大きく,前方走りはいい,そしてそれが出てくる,どう対処する?
ワイズCBこの戦略はEOSフューチャーに対応していますか?
空軍は決して奴隷ではない[json.exception.type_error.305] cannot use operator[] with a string argument with boolean. [json.exception.type_error.305] は, boolean との文字列引数で,[] を使えない ニュースで何が起こるか?
硬いexchange.SetContractType ((symbolA) は bool 型を返します. どうしたらいいですか?
発明者 量化 - 微かな夢第1のエラーはCoin type wrongであり,取引対が誤っているか確認し,通貨がどこかに設定されているか確認する.第2のエラーは,第1のエラーにより頻繁に再試し,取引所のインターフェースへのアクセス頻度制限を超えた結果である.
発明者 量化 - 微かな夢コード,あなたはデビュー,wsインタフェースreadのデータを見てください. 問題を探して,私はテストを正常にします.
アリンサーバーの問題ですか? しかし,このサーバーは,OKEXで取引できる取引端末です.
発明者 量化 - 微かな夢ネットワーク上の問題を説明します. 取引所に接続されていません. データが送られてません.
アリンK線なし 戦略グラフのみ /upload/asset/ba842a27a3766766bf54.png
発明者 量化 - 微かな夢ロボットが動作しているときに,ページにグラフが出ているか? グラフK線が出ているのは正常で,取引が触発されていない. グラフが出ていない場合は,市場の問題を示します.
発明者 量化 - 微かな夢実行してテストします. 実行してテストします.
リー・ライソンOKxのインターフェースは変わっていません.
発明者 量化 - 微かな夢このポリシーは,WSインターフェースに基づいているため,再テストをサポートしない.実況で,OKEXがWSインターフェースのポートを変更したかどうかを確認するには,ポリシーのコードに設定できます.
発明者 量化 - 微かな夢この完全なメッセージは,JSONを解析する際にWSインターフェイスが返したデータ異常によって引き起こされたはずである.
エルビス1213IPの問題は解決しました
発明者 量化 - 微かな夢OKEX WS インターフェースのアドレスが変更されたようです. OKEX 文書にアクセスして,現在何アドレスか確認するには,そのアドレスに入力することができます.
エルビス1213ありがとうございました
発明者 量化 - 微かな夢この戦略は主に学習,実用化,コードを理解し,原則を理解し,自分の取引習慣に基づいて最適化するために改変することを推奨します.
エルビス1213ありがとう,成功しました!
発明者 量化 - 微かな夢公開サーバーは,練習やテストのためにのみ使用します.
エルビス1213OKEXフューチャーが選択されました. サーバーに関連しているのですか? 私は公共サーバー /upload/asset/14b2c038dcb23dfa93b8b.jpgを選択しました.
発明者 量化 - 微かな夢交換オブジェクトを設定するときに,現貨に選択します. 再構成試行錯誤,選択するときに選択します: /upload/asset/178df7ad9e03924f4dda.png /upload/asset/178df7ad9e03924f4dda.png /upload/asset/178df7ad9e03924f4dda.png /upload/asset/178df7ad9e03924f4dda.png /upload/asset/178df7ad9e03924f4dda/
軽い雲ありがとう,私はIPをバインドして試してみました.
発明者 量化 - 微かな夢これは誤報ではなく,WSインターフェースのデータ異常であり,ポリシーが印刷した異常メッセージです.
発明者の量化更新管理が解決する
発明者 量化 - 微かな夢これは誤報ではなく,異常を捕まえた後に印刷されたメッセージで,青いログです. この出力は,必要ない場合は,コードから削除できます. JSONは,この問題を処理している. 管理者更新は可能です.
発明者 量化 - 微かな夢エラーメッセージ スクリーンショットを表示するか,情報をコピー・ペーストして送信します. エラーメッセージのパラメータが推定されています.