avatar of 发明者量化-小小梦 发明者量化-小小梦
konzentrieren Sie sich auf Private Nachricht
4
konzentrieren Sie sich auf
1271
Anhänger

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Erstellt in: 2019-08-26 14:30:47, aktualisiert am: 2024-12-17 20:43:39
comments   2
hits   3252

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Wenn es um Absicherungsstrategien geht, gibt es in verschiedenen Märkten eine Vielzahl von Strategien, eine Vielzahl von Kombinationen und eine Vielzahl von Ideen. Wir beginnen mit der klassischsten periodenübergreifenden Absicherung, um die Gestaltungsideen und Konzepte von Absicherungsstrategien zu erkunden. Heute herrscht auf dem Markt für digitale Währungen deutlich mehr Aktivität als zu seiner Entstehungszeit. Zudem sind zahlreiche Kontraktbörsen entstanden, die zahlreiche Arbitrage- und Absicherungsmöglichkeiten bieten. Es gibt unzählige Strategien, wie zum Beispiel Spot-Cross-Market-Arbitrage, Spot-Futures-Hedging-Arbitrage, Futures-Cross-Perioden-Arbitrage, Futures-Cross-Market-Arbitrage usw. Als nächstes werfen wir einen Blick auf eine „Hardcore“-Hedging-Strategie über mehrere Perioden hinweg, die in C++ geschrieben ist, wobei der Handelsmarkt die OKEX-Kontraktbörse ist. Die Strategie basiert auf der „Inventor Quantitative Trading Platform“.

  • ### Strategieprinzip

Der Grund für den gewissen Hardcore-Charakter der Strategie liegt darin, dass sie in der Programmiersprache C++ geschrieben ist, was das Lesen etwas erschwert. Dies hindert die Leser jedoch nicht daran, das Wesentliche dieses Strategieentwurfs und dieser Ideen kennenzulernen. Die Strategie ist durchweg relativ prägnant und die Codelänge moderat, lediglich mehr als 500 Zeilen. In Bezug auf die Marktdatenerfassung verwendet diese Strategie im Gegensatz zu früheren Strategien, die die Rest-Schnittstelle verwenden, die WebSocket-Schnittstelle, um Marktdaten-Pushs von Börsen zu empfangen. In Bezug auf das Design ist die Strategiestruktur sinnvoll, die Code-Kopplung ist sehr gering und eine Erweiterung oder Optimierung ist sehr bequem. Die logische Denkweise ist klar und dieses Design ist nicht nur einfach zu verwenden und zu erweitern. Ein gutes Beispiel für eine Lehrstrategie ist auch das Lernen von Strategiedesign. Das Prinzip der Strategie ist relativ einfach, nämlich die Verwendung von Terminkontrakten und kurzfristigen Kontrakten zur positiven und negativen Absicherung, was im Grunde dasselbe ist wie die periodenübergreifende Absicherung von Rohstofftermingeschäften. Positive Arbitrage, kurze Terminkontrakte und lange kurzfristige Kontrakte. Absicherung durch Long-Positionen bei Terminkontrakten und Short-Positionen bei kurzfristigen Kontrakten. Nachdem nun die Grundprinzipien geklärt sind, bleibt noch zu klären, wie die Strategie Absicherungspositionen auslöst, wie Positionen geschlossen und wie Positionen erhöht werden. Methoden zur Positionskontrolle und detaillierte Verarbeitung der Strategie. Die Absicherungsstrategie konzentriert sich hauptsächlich auf die Schwankung der Preisdifferenz des Basiswerts und führt Regressionshandel auf der Grundlage der Preisdifferenz durch. Der Spread kann jedoch leicht, stark oder in eine Richtung schwanken. Dies bringt zwar Unsicherheiten bei der Absicherung von Gewinnen und Verlusten mit sich, das Risiko ist jedoch immer noch weitaus geringer als bei einem einseitigen Trend. Viele der verschiedenen Optimierungen von periodenübergreifenden Strategien beginnen auf der Positionskontrollebene und starten bei den Auslösern für das Öffnen und Schließen. Beispielsweise wird der klassische Bollinger-Indikator als Eröffnungs- und Schlusspunkt für positive und negative Arbitrage verwendet, wenn die Preisdifferenz schwankt. Aufgrund ihrer sinnvollen Ausgestaltung und des geringen Kopplungsgrades lässt sich diese Strategie auch leicht in eine periodenübergreifende Absicherungsstrategie mit dem Bollinger-Indikator umwandeln.

  • ### Strategie-Code-Analyse

#### Wenn wir den Code im Allgemeinen betrachten, können wir feststellen, dass der Code hauptsächlich in vier Teile gegliedert ist.

  • 1. Definition des Aufzählungswerts. Definieren Sie einige Statuswerte, die zum Markieren des Status verwendet werden. Einige Funktionsfunktionen, die nicht mit der Strategie in Zusammenhang stehen, wie z. B. die URL-Codierungsfunktion, die Zeitkonvertierungsfunktion usw., haben nichts mit der Strategie zu tun und werden nur zur Datenverarbeitung verwendet.
  • 2. K-Line-Datengeneratorklasse: Die Strategie wird durch die vom Generatorklassenobjekt generierten K-Line-Daten gesteuert.
  • 3. Hedge-Klasse: Objekte dieser Klasse können spezifische Transaktionslogik, Hedging-Operationen, Mechanismen zur Verarbeitung von Strategiedetails usw. ausführen.
  • 4. Die Hauptfunktion der Strategie, nämlichmain Funktion.main Die Funktion ist die Einstiegsfunktion der Strategie. Die Hauptschleife wird in dieser Funktion ausgeführt. Darüber hinaus führt diese Funktion auch eine wichtige Operation aus, nämlich den Zugriff auf die WebSocket-Schnittstelle der Börse, um die gepushten Tick-Marktdaten als Rohdaten zu erhalten. Material des K-Linien-Datengenerators. Daten.

#### Indem wir den Strategiecode als Ganzes verstehen, können wir nun schrittweise jeden Link analysieren, um das Design, die Ideen und die Techniken der Strategie vollständig zu erlernen.

  • Definition des Aufzählungswerts, andere Funktionen

    1. AufzählungstypState Stellungnahme
    enum State {                    // 枚举类型  定义一些 状态
        STATE_NA,                   // 非正常状态
        STATE_IDLE,                 // 空闲
        STATE_HOLD_LONG,            // 持多仓
        STATE_HOLD_SHORT,           // 持空仓
    };
    

    Da einige Funktionen im Code einen bestimmten Status zurückgeben, werden diese Zustände im Aufzählungstyp definiertStateMitte. Sehen Sie, wie der Code angezeigt wirdSTATE_NA Das heißt, es handelt sich um einen anormalen Zustand.STATE_IDLE Es befindet sich in einem Leerlaufzustand, das heißt einem Zustand, in dem Absicherungsoperationen durchgeführt werden können.STATE_HOLD_LONG Der Zustand des Haltens einer positiven Absicherungsposition.STATE_HOLD_SHORT Der Zustand des Haltens einer Absicherungsposition.

    1. String-Ersetzung: Diese Funktion wird bei dieser Strategie nicht aufgerufen. Es handelt sich um eine Ersatzfunktion des Tools, die hauptsächlich Strings verarbeitet.
    string replace(string s, const string from, const string& to)   
    
    1. Funktion zur Konvertierung in hexadezimale ZeichentoHex
    inline unsigned char toHex(unsigned char x)
    
    1. Funktion zur Verarbeitung der URL-Kodierung
    std::string urlencode(const std::string& str)
    
    1. Zeitkonvertierungsfunktion, konvertiert die Zeit im Stringformat in einen Zeitstempel.
    uint64_t _Time(string &s)
    
  • K-Line-Datengeneratorklasse

    class BarFeeder {                                                                       // K线 数据生成器类
        public:
            BarFeeder(int period) : _period(period) {                                       // 构造函数,参数为 period 周期, 初始化列表中初始化
                _rs.Valid = true;                                                           // 构造函数体中初始化 K线数据的 Valid属性。
            }    
    
    
            void feed(double price, Chart *c=nullptr, int chartIdx=0) {                     // 输入数据,nullptr 空指针类型,chartIdx 索引默认参数为 0
                uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000;               // 秒级时间戳祛除不完整时间周期(不完整的_period 秒数),转为 毫秒级时间戳。
                bool newBar = false;                                                        // 标记 新K线Bar 的标记变量
                if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) {                    // 如果 K线数据 长度为 0 。 或者 最后一bar 的时间戳小于 epoch(K线最后一bar 比当前最近的周期时间戳还要靠前)
                    Record r;                                                               // 声明一个 K线bar 结构
                    r.Time = epoch;                                                         // 构造当前周期的K线bar 
                    r.Open = r.High = r.Low = r.Close = price;                              // 初始化 属性
                    _rs.push_back(r);                                                       // K线bar 压入 K线数据结构
                    if (_rs.size() > 2000) {                                                // 如果K线数据结构长度超过 2000 , 就剔除最早的数据。
                        _rs.erase(_rs.begin());
                    }
                    newBar = true;                                                          // 标记
                } else {                                                                    // 其它情况,不是出现新bar 的情况下的处理。
                    Record &r = _rs[_rs.size() - 1];                                        // 引用 数据中最后一bar 的数据。
                    r.High = max(r.High, price);                                            // 对引用数据的最高价更新操作。
                    r.Low = min(r.Low, price);                                              // 对引用数据的最低价更新操作。
                    r.Close = price;                                                        // 对引用数据的收盘价更新操作。
                }
    
    
                auto bar = _rs[_rs.size()-1];                                               // 取最后一柱数据 ,赋值给 bar 变量
                json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.Close};            // 构造一个 json 类型数据
                if (c != nullptr) {                                                         // 图表对象指针不等于 空指针,执行以下。
                   if (newBar) {                                                            // 根据标记判断,如果出现新Bar 
                        c->add(chartIdx, point);                                            // 调用图表对象成员函数add,向图表对象中插入数据(新增K线bar)
                        c->reset(1000);                                                     // 只保留1000 bar的数据
                    } else {
                        c->add(chartIdx, point, -1);                                        // 否则就更新(不是新bar),这个点(更新这个bar)。
                    } 
                }
            }
            Records & get() {                                                               // 成员函数,获取K线数据的方法。
                return _rs;                                                                 // 返回对象的私有变量 _rs 。(即 生成的K线数据)
            }
        private:
            int _period;
            Records _rs;
    };
    

    Diese Klasse ist hauptsächlich für die Verarbeitung der erfassten Tick-Daten in Preisdifferenz-K-Linien verantwortlich, um die Strategie-Hedging-Logik voranzutreiben. Einige Leser fragen sich vielleicht, warum wir Tick-Daten verwenden müssen. Warum müssen wir einen solchen K-Linien-Datengenerator konstruieren? Ist es nicht besser, K-Line-Daten direkt zu verwenden? Diese drei Fragen sind mir in den Sinn gekommen, als ich einige Absicherungsstrategien geschrieben habe. Die Antwort habe ich gefunden, als ich die Spread-Bollinger-Hedging-Strategie geschrieben habe. Denn die K-Line-Daten eines einzelnen Kontrakts stellen die Preisänderungsstatistik dieses Kontrakts innerhalb eines bestimmten Zeitraums dar. Die K-Liniendaten der Preisdifferenz zwischen zwei Verträgen sind die Statistik der Preisdifferenz innerhalb eines bestimmten Zeitraums. Daher können wir nicht einfach die K-Liniendaten der beiden Verträge subtrahieren, um die Differenz der einzelnen Daten auf jedem K- zu berechnen. Linie Balken. Wert, als Preisdifferenz. Der offensichtlichste Fehler besteht darin, dass der höchste und der niedrigste Preis zweier Kontrakte nicht unbedingt gleichzeitig auftreten. Daher macht der subtrahierte Wert nicht viel Sinn. Daher müssen wir Tick-Daten in Echtzeit verwenden, die Preisdifferenz in Echtzeit berechnen und Statistiken über die Preisänderungen innerhalb eines bestimmten Zeitraums in Echtzeit erstellen (d. h. den hohen Eröffnungs- und niedrigen Schlusskurs in der K-Linien-Spalte). Auf diese Weise benötigen wir einen K-Line-Datengenerator als separate Klasse, um die Verarbeitungslogik zu trennen.

  • Absicherung

    class Hedge {                                                                           // 对冲类,策略主要逻辑。
      public:
        Hedge() {                                                                           // 构造函数
            ...
        };
    
    
        State getState(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB) {        // 获取状态,参数: 合约A名称 、合约A深度数据, 合约B名称、 合约B深度数据
    
    
            ...
        }
        bool Loop(string &symbolA, Depth &depthA, string &symbolB, Depth &depthB, string extra="") {       // 开平仓 策略主要逻辑
    
    
            ...
        }    
    
    
      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);            // A合约 行情 K线生成器
        BarFeeder _feederB = BarFeeder(DPeriod);            // B合约 行情 K线生成器
        State _st = STATE_NA;                               // 对冲类型 对象的 对冲持仓状态
        string _cfgStr;                                     // 图表配置 字符串
        double _holdAmount = 0;                             // 持仓量
        bool _isCover = false;                              // 是否平仓 标记
        bool _needCheckOrder = true;                        // 设置是否 检查订单
        Chart _c = Chart("");                               // 图表对象,并初始化
    };
    
    
    

    Da der Code recht lang ist, wird ein Teil davon weggelassen. Er zeigt hauptsächlich die Struktur der Hedge-Klasse. Der Konstruktor Hedge wird nicht erwähnt, da er hauptsächlich zur Objektinitialisierung dient. Es bleiben im Wesentlichen zwei Funktionen übrig.

    • getState

    Diese Funktion befasst sich hauptsächlich mit der Auftragserkennung, Auftragsstornierung, Positionserkennung, dem Positionsausgleich usw. Denn im Prozess der Absicherung von Transaktionen ist die Single-Leg-Situation (d. h. ein Kontrakt wird gehandelt und der andere nicht) unvermeidbar. Wird sie in der Orderlogik erkannt und dann die Folgeorder oder Positionsschließung verarbeitet , wird die Strategielogik chaotisch sein. Deshalb haben wir beim Entwurf dieses Teils einen anderen Ansatz gewählt. Wenn die Hedging-Operation ausgelöst wird, wird einmalig eine Order platziert. Unabhängig davon, ob ein Single-Leg-Hedging stattfindet, gilt das Hedging standardmäßig als erfolgreich. Anschließend wird der Positionssaldo in der getState-Funktion geprüft und die Logik zur Prüfung und Verarbeitung der Der Restbetrag wird abgetrennt.

    • Loop

    Die Handelslogik der Strategie ist in dieser Funktion gekapselt, wobei der AufrufgetState , verwenden Sie das K-Line-Datengeneratorobjekt, um die K-Line-Daten der Preisdifferenz zu generieren und treffen Sie Urteile über die Logik der Absicherung beim Öffnen, Schließen und Hinzufügen von Positionen. Es gibt auch einige Datenaktualisierungsvorgänge für Diagramme.

  • Hauptfunktion der Strategie

    void main() {  
    
    
        ...
    
    
        string realSymbolA = exchange.SetContractType(symbolA)["instrument"];    // 获取设置的A合约(this_week / next_week / quarter ) ,在 OKEX 合约 当周、次周、季度 对应的真实合约ID 。
        string realSymbolB = exchange.SetContractType(symbolB)["instrument"];    // ...
    
    
        string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump());    // 对 ws 接口的要传的参数进行 json 编码、 url 编码
        Log("try connect to websocket");                                                                                                            // 打印连接 WS接口的信息。
        auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs);     // 调用FMZ API Dial 函数 访问  OKEX 期货的 WS 接口
        Log("connect to websocket success");
    
    
        Depth depthA, depthB;                               // 声明两个 深度数据结构的变量 用于储存A合约和B合约 的深度数据
        auto fillDepth = [](json &data, Depth &d) {         // 用接口返回的json 数据,构造 Depth 数据的代码。
            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;   // 时间 字符串 A 
        string timeB;   // 时间 字符串 B 
        while (true) {
            auto buf = ws.read();                           // 读取 WS接口 推送来的数据
    
    
            ...
    
    
    }
    

    Nachdem die Strategie gestartet wurde, beginnt sie mit der Ausführung von der Hauptfunktion aus. Während der Initialisierung der Hauptfunktion abonniert die Strategie den Tick-Markt der WebSocket-Schnittstelle. Die Hauptaufgabe der Hauptfunktion besteht darin, eine Hauptschleife zu erstellen, kontinuierlich Tick-Informationen zu empfangen, die von der WebSocket-Schnittstelle der Börse gesendet werden, und dann die Mitgliedsfunktion des Hedge-Klassenobjekts aufzurufen: die Loop-Funktion. Die Handelslogik in der Loop-Funktion wird von Marktdaten gesteuert. Eine Sache, die erklärt werden muss, ist, dass es sich bei dem oben erwähnten Tick-Markt tatsächlich um die abonnierte Orderbuch-Tiefendatenschnittstelle handelt, die die Orderbuchdaten jeder Ebene abruft. Die Strategie verwendet jedoch nur die Daten der ersten Ebene, die tatsächlich den Tick-Marktdaten ähneln. Die Strategie verwendet weder Daten anderer Ebenen noch die Ordervolumenwerte der ersten Ebene. Schauen wir uns genauer an, wie die Strategie die Daten der WebSocket-Schnittstelle abonniert und wie diese eingestellt werden.

    string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump());    
    Log("try connect to websocket");                                                                                                            
    auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs);     
    Log("connect to websocket success");
    

    Zunächst müssen Sie den von der Abonnementschnittstelle übertragenen JSON-Parameter der Abonnementnachricht URL-kodieren, d. h.payload Der Wert des Parameters. Der wichtigere Schritt besteht dann darin, die API-Schnittstellenfunktion der Inventor Quantitative Trading Platform aufzurufen.Dial Funktion.Dial Mit der Funktion kann auf die Exchange-WebSocket-Schnittstelle zugegriffen werden. Wir nehmen hier einige Einstellungen vor, um das WebSocket-Verbindungssteuerungsobjekt ws zu aktivieren, das nach der Trennung automatisch eine erneute Verbindung herstellt (die Abonnementnachricht verwendet weiterhin den Wert qs des Payload-Parameters). Um diese Funktion zu erreichen, müssen SieDial Fügen Sie der Parameterzeichenfolge der Funktion Konfigurationsoptionen hinzu.

    DialDie Funktionsparameter beginnen wie folgt:

    wss://real.okex.com:10442/ws/v3
    

    Ist die WebSocket-Schnittstellenadresse, auf die zugegriffen werden muss, und dann verwendet| Trennung. compress=gzip_raw&mode=recv&reconnect=true&payload="+qs Dies sind alles Konfigurationsparameter.

    Parametername Beschreibung
    compress compress ist die Komprimierungsmethode. Die OKEX-WebSocket-Schnittstelle verwendet gzip_raw, daher ist sie auf gzip_raw eingestellt
    mode mode ist der Modus und die Optionen sind Dual, Senden und Empfangen. Dual bedeutet bidirektional, Senden und Empfangen komprimierter Daten. Senden dient zum Senden komprimierter Daten. recv empfängt komprimierte Daten und dekomprimiert sie lokal.
    reconnect reconnect gibt an, ob die erneute Verbindung festgelegt werden soll. reconnect=true aktiviert die erneute Verbindung. Wenn es nicht festgelegt ist, ist die erneute Verbindung standardmäßig deaktiviert.
    payload Payload ist die Abonnementnachricht, die gesendet werden muss, wenn ws die Verbindung erneut herstellt.

    Nach dieser Einstellung stellt das zugrunde liegende System des Depotverwalters der Inventor Quantitative Trading Platform automatisch die Verbindung wieder her und ruft rechtzeitig die neuesten Marktdaten ab, auch wenn die WebSocket-Verbindung getrennt wird. Nutzen Sie jede Preisdifferenzschwankung und erobern Sie schnell den passenden Absicherungsmarkt.

  • Positionsregelung

Bei der Positionskontrolle wird zur Steuerung ein Hedge-Position-Verhältnis ähnlich der „Porfinacci“-Reihe verwendet.

  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]);   // 最后 两个加仓数量相加,算出当前的加仓数量储存到 _addArr数据结构中。
  }

Es ist ersichtlich, dass die Anzahl der jedes Mal hinzugefügten Positionen die Summe der beiden letzten Positionen ist. Eine solche Positionskontrolle kann erreichen, dass je größer der Preisunterschied ist, desto größer ist der Betrag der Arbitrageabsicherung, und die Positionen können gestreut werden, um die kleinen Preisunterschiedsschwankungen mit kleinen Positionen zu erfassen und die Positionen mit großen Preisunterschieden entsprechend zu erhöhen. Schwankungen.

  • ### Schließen einer Position: Stop-Loss, Take-Profit

Fester Take-Profit-Spread, Stop-Loss-Spread. Wenn die Preisdifferenz der Position die Take-Profit-Position oder Stop-Loss-Position erreicht, wird Take Profit oder Stop Loss ausgeführt.

  • ### Eintritt in und Austritt aus dem Marktzyklusdesign

Der durch den Parameter NPeriod gesteuerte Zeitraum bietet ein gewisses Maß an dynamischer Kontrolle über das Öffnen und Schließen von Positionen der Strategie.

  • ### Strategiediagramm

Die Strategie generiert automatisch ein Candlestick-Diagramm der Preisdifferenzen und markiert relevante Transaktionsinformationen.

Auch die benutzerdefinierte Diagrammzeichnung mit der C++-Strategie ist sehr einfach. Sie können sehen, dass wir im Konstruktor der Hedge-Klasse die geschriebene Diagrammkonfigurationszeichenfolge _cfgStr verwenden, um das Diagrammobjekt _c zu konfigurieren._c ist die Hedge-Klasse. Wenn das private Mitglied initialisiert wird, wird die quantitative API-Schnittstellenfunktion des Erfinders für benutzerdefinierte Diagramme aufgerufen.Chart Das von der Funktion erstellte Diagrammobjekt.

  _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();                         // 重置图表数据。
  • Anruf_c.update(_cfgStr); verwenden _cfgStr-Konfiguration zum Chart-Objekt.
  • Anruf_c.reset(); Diagrammdaten zurücksetzen.

Wenn der Strategiecode Daten in das Diagramm einfügen muss, wird er auch direkt aufgerufen_Mitgliedsfunktion eines c-Objekts oder_Der Verweis auf c wird als Parameter übergeben, und dann wird die Objektmitgliedsfunktion (Methode) von _c aufgerufen, um Aktualisierungs- und Einfügevorgänge für die Diagrammdaten durchzuführen. Zum Beispiel:

  _c.add(chartIdx, {{"x", UnixNano()/1000000}, {"title", action},  {"text", format("diff: %f", opPrice)}, {"color", color}});

Nachdem Sie eine Bestellung aufgegeben haben, markieren Sie diese im Candlestick-Chart.

Wie unten gezeigt, erfolgt das Zeichnen der K-Linie durch Aufrufen der Mitgliedsfunktion der BarFeeder-Klassefeed Wenn das Diagrammobjekt_Als Parameter wird eine Referenz auf c übergeben.

  void feed(double price, Chart *c=nullptr, int chartIdx=0)

Im AugenblickfeedDer Funktionsparameter c.

  json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.Close};            // 构造一个 json 类型数据
  if (c != nullptr) {                                                         // 图表对象指针不等于 空指针,执行以下。
     if (newBar) {                                                            // 根据标记判断,如果出现新Bar 
          c->add(chartIdx, point);                                            // 调用图表对象成员函数add,向图表对象中插入数据(新增K线bar)
          c->reset(1000);                                                     // 只保留1000 bar个数据
      } else {
          c->add(chartIdx, point, -1);                                        // 否则就更新(不是新bar),这个点(更新这个bar)。
      } 
  }

Durch Aufruf des Chart-Objekts_CaddMitgliedsfunktion, fügt neue K-Line-Balkendaten in das Diagramm ein. Code:c->add(chartIdx, point);

  • ### Backtesting

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Lernen Sie die Hardcore-Strategie der „OKEX-Vertragsabsicherungsstrategie in C++“

Diese Strategie dient nur zum Lernen und zur Kommunikation. Wenn Sie sie im realen Handel verwenden, ändern und optimieren Sie sie bitte entsprechend der tatsächlichen Handelssituation.

Strategieadresse: https://www.fmz.com/strategy/163447

Weitere interessante Strategien finden Sie auf der „Inventor Quantitative Trading Platform“: https://www.fmz.com