"C++-Version der OKEX-Futures-Kontrakt-Hedging-Strategie", die Sie durch eine harte quantitative Strategie führt.

Schriftsteller:Gutes, Erstellt: 2019-08-29 16:05:07, aktualisiert: 2023-11-07 20:53:27

img

Wenn wir von Hedging-Strategien sprechen, gibt es verschiedene Arten, verschiedene Kombinationen und verschiedene Ideen in verschiedenen Märkten. Wir erforschen die Designideen und Konzepte der Hedging-Strategie aus der klassischsten intertemporalen Hedging. Heute ist der Kryptowährungsmarkt viel aktiver als zu Beginn, und es gibt auch viele Futures-Kontraktbörsen, die viele Möglichkeiten für die Arbitrage-Hedging bieten. Spot-Markt-Arbitrage, Cash-Hedge-Arbitrage, Futures-Intertemporal-Arbitrage, Futures-Markt-Arbitrage usw.

Strategieprinzip

Warum ist die Strategie etwas Hardcore, weil die Strategie in C++ geschrieben ist und die Strategielesung etwas schwieriger ist. Aber das hindert die Leser nicht daran, die Essenz dieses Strategiedesigns und Ideen zu erlernen. Die Strategielogik ist relativ einfach, die Codelänge ist moderat, nur 500 Zeilen. In Bezug auf die Marktdatenerfassung, im Gegensatz zu den anderen Strategien, die die rest-Schnittstelle verwenden.

In Bezug auf das Design ist die Strategiestruktur vernünftig, der Code-Koppelungsgrad ist sehr niedrig und es ist bequem, zu erweitern oder zu optimieren. Die Logik ist klar, und ein solches Design ist nicht nur leicht zu verstehen. Als Lehrmaterial ist auch das Lernen dieser Strategies Design ein gutes Beispiel. Das Prinzip dieser Strategie ist relativ einfach, dh ist die Spread des Forward-Kontrakts und der jüngsten Vertrag positiv oder negativ? das Grundprinzip entspricht der intertemporalen Absicherung von Rohstoff-Futures.

  • Spread Positive, Verkauf von kurzfristigen Fremdkontrakte, Kauf von langen jüngsten Kontrakte.
  • Negative Spread, Kauf von langen Forward-Kontrakten, Verkauf von kürzlich geschlossenen Verträgen.

Nach dem Verständnis der Grundprinzipien ist der Rest, wie die Strategie die Eröffnungsposition der Absicherung auslöst, wie die Position geschlossen wird, wie Positionen hinzugefügt werden, Gesamtpositionskontrollmethode und andere Strategie-Detailverarbeitung.

Die Absicherungsstrategie befasst sich hauptsächlich mit der Schwankung der Gegenstandspreisdifferenz (The Spread) und ihrer Regression.

Dies bringt Unsicherheit über die Absicherung von Gewinnen und Verlusten mit sich, aber das Risiko ist immer noch viel geringer als der einseitige Trend. Für die verschiedenen Optimierungen der intertemporalen Strategie können wir wählen, ob wir von der Position kontrollieren Niveau und der Eröffnungs- und Schließung Trigger-Bedingung beginnen. zum Beispiel können wir den klassischen Bollinger Band Indicator verwenden, um die Kursschwankung zu bestimmen. Aufgrund des vernünftigen Designs und niedrigen Kopplungsgrades kann diese Strategie leicht in die Bollinger Index intertemporalen Absicherungsstrategie modifiziert werden

Analyse des Strategiecodes

Wenn man sich den gesamten Code anschaut, kann man schließen, dass der Code grob in vier Teile unterteilt ist.

  1. Einige Funktionen, die nicht mit der Strategie zusammenhängen, wie z. B. URL-Codierungsfunktionen, Zeitkonvertierungsfunktionen usw., haben keine Beziehung zur Strategielogik, nur für die Datenverarbeitung.

  2. K-Linie-Datengeneratorklasse: Die Strategie wird von den K-Linie-Daten bestimmt, die vom Generatorklasseobjekt generiert werden.

  3. Absicherungsklasse: Objekte dieser Klasse können spezifische Handelslogik, Absicherungsvorgänge und Verarbeitungsdetails der Strategie durchführen.

  4. Die Hauptfunktion der Strategie, die main Funktion ist. Die Hauptfunktion ist die Eingabefunktion der Strategie. Die Hauptschleife wird innerhalb dieser Funktion ausgeführt. Darüber hinaus führt diese Funktion auch eine wichtige Operation durch, nämlich den Zugriff auf die Websocket-Schnittstelle der Börse und die Erfassung der gedrängten Roh-Tick-Marktdaten als K-Line-Datengenerator.

Durch das allgemeine Verständnis des Strategiecodes können wir allmählich die verschiedenen Aspekte der Strategie erlernen und dann das Design, die Ideen und die Fähigkeiten der Strategie studieren.

  • Definition der Aufzählwerte, sonstige Funktionsfunktionen
  1. aufgezählte ArtStateAuskunft
enum State {                    // Enum type defines some states
    STATE_NA,                   // Abnormal state
    STATE_IDLE,                 // idle
    STATE_HOLD_LONG,            // holding long positions
    STATE_HOLD_SHORT,           // holding short positions
};

Da einige Funktionen im Code einen Zustand zurückgeben, werden diese Zustände im Aufzählungstyp definiertState.

Das zu sehen.STATE_NAim Code angezeigt wird, ist abnormal, undSTATE_IDLEist inaktiv, d. h. der Betriebszustand kann abgesichert werden.STATE_HOLD_LONGist der Zustand, in dem die positive Sicherungsposition gehalten wird.STATE_HOLD_SHORTist der Zustand, in dem die negative Sicherungsposition gehalten wird.

  1. String Substitution, die in dieser Strategie nicht aufgerufen wird, ist eine alternative Dienstleistungsfunktion, die sich hauptsächlich mit Zeichenfolgen befasst.
string replace(string s, const string from, const string& to)
  1. Eine Funktion zur Umwandlung in HexadezimalzeichentoHex
inline unsigned char toHex(unsigned char x)
  1. Handhabung von URL-kodierten Funktionen
std::string urlencode(const std::string& str)
  1. Eine Zeitkonvertierungsfunktion, die die Zeit im Zeichenkettenformat in einen Zeitstempel umwandelt.
uint64_t _Time(string &s)
  • Klasse des Datengenerators für die Linie K
class BarFeeder { // K line data generator class
    public:
        BarFeeder(int period) : _period(period) { // constructor with argument "period" period, initialized in initialization list
            _rs.Valid = true; // Initialize the "Valid" property of the K-line data in the constructor body.
        }

        void feed(double price, chart *c=nullptr, int chartIdx=0) { // input data, "nullptr" null pointer type, "chartIdx" index default parameter is 0
            uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000; // The second-level timestamp removes the incomplete time period (incomplete _period seconds) and is converted to a millisecond timestamp.
            bool newBar = false; // mark the tag variable of the new K line Bar
            if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) { // if the K line data is 0 in length. Or the last bar's timestamp is less than epoch (the last bar of the K line is more than the current most recent cycle timestamp)
                record r; // declare a K line bar structure
                r.Time = epoch; // construct the K line bar of the current cycle
                r.Open = r.High = r.Low = r.close = price; // Initialize the property
                _rs.push_back(r); // K line bar is pressed into the K line data structure
                if (_rs.size() > 2000) { // if the K-line data structure length exceeds 2000, the oldest data is removed.
                    _rs.erase(_rs.begin());
                }
                newBar = true; // tag
            } else { // In other cases, it is not the case of a new bar.
                record &r = _rs[_rs.size() - 1]; // Reference the data of the last bar in the data.
                r.High = max(r.High, price); // The highest price update operation for the referenced data.
                r.Low = min(r.Low, price); // The lowest price update operation for the referenced data.
                r.close = price; // Update the closing price of the referenced data.
            }
    
            auto bar = _rs[_rs.size()-1]; // Take the last column data and assign it to the bar variable
            json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // construct a json type data
            if (c != nullptr) { // The chart object pointer is not equal to the null pointer, do the following.
               if (newBar) { // judge if the new Bar appears
                    c->add(chartIdx, point); // call the chart object member function add to insert data into the chart object (new k line bar)
                    c->reset(1000); // retain only 1000 bar of data
                } else {
                    c->add(chartIdx, point, -1); // Otherwise update (not new bar), this point (update this bar).
                }
            }
        }
        records & get() { // member function, method for getting K line data.
            Return _rs; // Returns the object's private variable _rs . (ie generated K-line data)
        }
    private:
        int _period;
        records _rs;
};

Diese Klasse ist hauptsächlich für die Verarbeitung der erfassten Tick-Daten in eine Differenz-K-Linie für die Steuerung der Strategie-Hedging-Logik verantwortlich.

Einige Leser mögen Fragen haben, warum Tick-Daten verwenden? Warum einen K-Liniendatengenerator wie diesen konstruieren? Ist es nicht gut, K-Liniendaten direkt zu verwenden? Diese Art von Frage wurde in drei Ausbrüchen gestellt. Als ich einige Hedging-Strategien schrieb, machte ich auch Aufruhr. Ich fand die Antwort, als ich die Bollinger-Hedge-Strategie schrieb. Da die K-Liniendaten für einen einzelnen Vertrag die Preisänderungsstatistik für diesen Vertrag über einen bestimmten Zeitraum sind.

Die K-Liniendaten der Differenz zwischen den beiden Verträgen sind die Unterschiedspreisänderungsstatistiken in einem bestimmten Zeitraum. Daher ist es nicht möglich, einfach die K-Liniendaten jedes der beiden Verträge zur Subtraktion zu nehmen und die Differenz jeder Daten auf jeder K-Linienbalke zu berechnen. Der offensichtlichste Fehler ist zum Beispiel der höchste Preis und der niedrigste Preis von zwei Verträgen, nicht unbedingt gleichzeitig. Der subtrahierte Wert macht also nicht viel Sinn.

Daher müssen wir Echtzeit-Tick-Daten verwenden, um die Differenz in Echtzeit zu berechnen und die Preisänderung in einem bestimmten Zeitraum in Echtzeit zu berechnen (d. h. der höchste, niedrigste, offene und schließende Preis in der K-Linie Spalte).

  • Sicherungsklasse
class Hedge { // Hedging class, the main logic of the strategy.
  public:
    Hedge() { // constructor
        ...
    };
    
    State getState(string &symbolA, depth &depthA, string &symbolB, depth &depthB) { // Get state, parameters: contract A name, contract A depth data, contract B name, contract B depth data
        
        ...
    }
    bool Loop(string &symbolA, depth &depthA, string &symbolB, depth &depthB, string extra="") { // Opening and closing position main logic
        
        ...
    }

  private:
    vector<double> _addArr; // Hedging adding position list
    string _state_desc[4] = {"NA", "IDLE", "LONG", "SHORT"}; // Status value Description
    int _countOpen = 0; // number of opening positions
    int _countcover = 0; // number of closing positions
    int _lastcache = 0; //
    int _hedgecount = 0; // number of hedging
    int _loopcount = 0; // loop count (cycle count)
    double _holdPrice = 0; // holding position price
    BarFeeder _feederA = BarFeeder(DPeriod); // A contract Quote K line generator
    BarFeeder _feederB = BarFeeder(DPeriod); // B contract Quote K line generator
    State _st = STATE_NA; // Hedging type Object Hedging position status
    string _cfgStr; // chart configuration string
    double _holdAmount = 0; // holding position amount
    bool _iscover = false; // the tag of whether to close the position
    bool _needcheckOrder = true; // Set whether to check the order
    chart _c = chart(""); // chart object and initialize
};

Da der Code relativ lang ist, werden einige Teile weggelassen, dies zeigt hauptsächlich die Struktur dieser Hedge-Klasse, der Konstruktor Hedge-Funktion wird weggelassen, hauptsächlich zum Zweck der Objektinitialisierung.

GetState

Diese Funktion befasst sich hauptsächlich mit der Auftragsinspektion, Auftragsauslösung, Positionserkennung, Positionsausgleich usw. Da es im Prozess der Absicherungstransaktionen unmöglich ist, ein einzelnes Bein zu vermeiden (d. h. ein Vertrag wird ausgeführt, ein anderer nicht), wenn die Prüfung in der Platzierungsorderlogik durchgeführt wird, und dann die Verarbeitung der Auftragsre-Send-Operation oder der Positionsabschlussoperation, wird die Strategie-Logik chaotisch sein.

Wenn die Absicherungsaktion ausgelöst wird, solange der Auftrag einmal platziert wird, unabhängig davon, ob es eine Einbein-Absicherung gibt, ist der Standard, dass die Absicherung erfolgreich ist, und dann wird der Positionssaldo in dergetStateDie Funktion und die Logik für die Bearbeitung des Saldos werden unabhängig voneinander behandelt.

Schleife

Die Handelslogik der Strategie ist in dieser Funktion zusammengefaßt, in dergetStateEs gibt auch einige Datenaktualisierungsvorgänge für das Diagramm.

  • Hauptfunktion der Strategie
void main() {

    ...
    
    string realSymbolA = exchange.SetcontractType(symbolA)["instrument"]; // Get the A contract (this_week / next_week / quarter ), the real contract ID corresponding to the week, next week, and quarter of the OKEX futures contract.
    string realSymbolB = exchange.SetcontractType(symbolB)["instrument"]; // ...
    
    string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump()) ; // jSON encoding, url encoding for the parameters to be passed on the ws interface
    Log("try connect to websocket"); // Print the information of the connection WS interface.
    auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs); // call the FMZ API "Dial" function to acess the WS interface of OKEX Futures
    Log("connect to websocket sucess");
    
    depth depthA, depthB; // Declare two variables of the depth data structure to store the depth data of the A contract and the B contract
    auto filldepth = [](json &data, depth &d) { // construct the code for the depth data with the json data returned by the interface.
        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; // time string A
    string timeB; // time string B
    while (true) {
        auto buf = ws.read(); // Read the data pushed by the WS interface
        
        ...
        
}

Nach dem Starten der Strategie wird sie von der Hauptfunktion aus ausgeführt. Bei der Initialisierung der Hauptfunktion abonniert die Strategie den Tick-Markt der Websocket-Schnittstelle. Die Hauptaufgabe der Hauptfunktion besteht darin, eine Hauptschleife zu konstruieren, die kontinuierlich die Tick-Koten empfängt, die von der Websocket-Schnittstelle der Börse gedrückt werden, und dann die Mitgliederfunktion des Hedge-Klassenobjektes: Loop-Funktion aufruft. Die Handelslogik in der Loop-Funktion wird von den Marktdaten angetrieben.

Ein Punkt zu beachten ist, dass der oben erwähnte Tick-Markt eigentlich die Abonnement-Order-Durchgründungs-Datenoberfläche ist, die die Bestellbuchdaten für jede Datei ist. Die Strategie verwendet jedoch nur die erste Datei von Daten, in der Tat ist sie fast gleich den Tick-Marktdaten. Die Strategie verwendet weder die Daten anderer Dateien noch den Bestellwert der ersten Datei.

Schauen Sie sich genauer an, wie die Strategie die Daten der Websocket-Schnittstelle abonniert und wie sie eingerichtet ist.

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 sucess");

Zunächst wird die URL-Codierung der Abonnement-Nachricht json Parameter durch die abonnierte Schnittstelle übergeben, dh der Wert derpayloadDann ist ein wichtiger Schritt, die API-Schnittstellenfunktion der FMZ Quant-Plattform aufzurufen.DialDieDialHier machen wir einige Einstellungen, lassen Sie die Websocket-Verbindungsteuerung Objekt ws erstellt werden, haben automatische Wiederverbindung der Trennung (die Abonnement-Nachricht verwendet immer noch den WertqsString derpayloadParameter), um diese Funktion zu erreichen, müssen Sie Konfiguration in der Parameter-String derDial function.

Der Beginn derDialFunktionsparameter ist:

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

Dies ist die Adresse der Websocket-Schnittstelle, auf die zugegriffen werden muss, und ist durch getrennt.

compress=gzip_raw&mode=recv&reconnect=true&payload="+qssind alle Konfigurationsparameter.

Name des Parameters Beschreibung
Kompressen Komprimieren ist Komprimierungsmodus, OKEX Websocket-Schnittstelle verwendet gzip_raw auf diese Weise, so ist es auf gzip_raw gesetzt
Modus Modus ist Modus, optional dual, send und recv drei Art. Dual ist bidirektional, das Senden komprimierter Daten und das Empfangen komprimierter Daten. Send ist das Senden komprimierter Daten. Recv empfängt die komprimierten Daten und dekomprimiert sie lokal.
Wiederanschluss Reconnect ist auf Reconnect gesetzt, reconnect=true, um die Wiederverbindung zu ermöglichen, kein Standard wird nicht wieder verbunden.
Nutzlast Die Nutzlast ist eine Abonnement-Nachricht, die gesendet werden muss, wenn ws wieder verbunden wird.

Nach dieser Einstellung wird sich das zugrunde liegende System des Docker-Systems der FMZ Quant-Handelsplattform automatisch wieder verbinden und rechtzeitig die neuesten Marktdaten erhalten, auch wenn die Websocket-Verbindung getrennt ist.

Schnappen Sie sich jede Preisschwankung und erfassen Sie schnell die richtige Absicherung.

  • Positionskontrolle

Die Positionskontrolle erfolgt anhand eines Verhältnisses der Absicherungspositionen, das der Fibonacci-Reihe ähnelt.

for (int i = 0; i < AddMax + 1; i++) { // construct a data structure that controls the number of scalping, similar to the ratio of the Bofinac sequence to the number of hedges.
     if (_addArr.size() < 2) { // The first two added positions are changed as: double the number of hedges
         _addArr.push_back((i+1)*OpenAmount);
     }
     _addArr.push_back(_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); // The last two adding positions are added together, and the current position quantity is calculated and stored in the "_addArr" data structure.
}

Es kann festgestellt werden, daß die Zahl der jeweils hinzugefügten zusätzlichen Positionen die Summe der beiden letzten Positionen ist.

Eine solche Positionskontrolle kann die größere Differenz, die relative Erhöhung der Arbitrageabsicherung und die Dispersion der Position realisieren, um die kleine Position der kleinen Preisschwankung zu erfassen und die große Preisschwankungsposition angemessen zu erhöhen.

  • Schließungsposition: Stop-Loss und Take-Profit

Festgelegte Stop-Loss- und Take-Profit-Spread.

Wenn die Positionsdifferenz die Take-Profit-Position und die Stop-Loss-Position erreicht, werden der Take-Profit und der Stop-Loss ausgeführt.

  • Die Planung des Markteintritts und des Marktabgangs

Periode des ParametersNPeriodDie Strategie wird in der Regel durch eine dynamische Steuerung der Eröffnungs- und Schließposition der Strategie gesteuert.

  • Strategieplan

Die Strategie erzeugt automatisch ein Spread-K-Liniendiagramm, um relevante Transaktionsinformationen zu markieren.

Sie können sehen, dass im Konstruktor der Absicherungsklasse, verwenden wir die geschriebene Chart-Konfigurations-String_cfgStrUm das Chartobjekt zu konfigurieren_c, _cist die private Komponente der Absicherungsklasse.chartObjekt, das von der FMZ Quant-Plattform konstruiert wurde, wird mit der benutzerdefinierten Diagramm-API-Schnittstellenfunktion aufgerufen.

_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);                 // Update chart objects with chart configuration
_c.reset();                         // Reset chart data。
call _c.update(_cfgStr); Use _cfgStr to configure to the chart object.

call _c.reset(); to reset the chart data.

Wenn der Strategie-Code Daten in das Diagramm einfügen muss, ruft er auch die Mitgliederfunktion des Diagramms an._cObjekt direkt, oder überträgt die Referenz von_cals Parameter und ruft dann die Objekt-Mitgliedsfunktion (Methode) von_cdie Daten des Diagramms zu aktualisieren und die Operation einzufügen.

Zum Beispiel:

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

Nach der Bestellung markieren Sie das K-Liniendiagramm.

Folgendermaßen, wenn eine K-Linie gezogen wird, eine Bezugnahme auf das Chart-Objekt_cwird als Parameter übergeben, wenn die Mitgliederfunktion aufgerufen wirdfeedderBarFeeder class.

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

Das heißt, der formale Parametercderfeed function.

json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // construct a json type data
if (c != nullptr) { // The chart object pointer is not equal to the null pointer, do the following.
    if (newBar) { // judge if the new Bar appears
         c->add(chartIdx, point); // call the chart object member function "add" to insert data into the chart object (new k line bar)
         c->reset(1000); // only keep 1000 bar data
     } else {
         c->add(chartIdx, point, -1); // Otherwise update (not new bar), this point (update this bar).
     }
}

Einfügen Sie eine neue K-Linie Bar Daten in das Diagramm, indem Sie dieaddFunktion des Chartobjekts_c.

c->add(chartIdx, point);

Zurückprüfung

img img img

Diese Strategie dient ausschließlich Lern- und Kommunikationszwecken.

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

Interessantere Strategien finden Sie auf der FMZ Quant-Plattform":https://www.fmz.com


Verwandt

Mehr