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.
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
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 Strategie
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
Wenn man sich den gesamten Code anschaut, kann man schließen, dass der Code grob in vier Teile unterteilt ist.
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.
K-Linie-Datengeneratorklasse: Die Strategie wird von den K-Linie-Daten bestimmt, die vom Generatorklasseobjekt generiert werden.
Absicherungsklasse: Objekte dieser Klasse können spezifische Handelslogik, Absicherungsvorgänge und Verarbeitungsdetails der Strategie durchführen.
Die Hauptfunktion der Strategie, die
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.
State
Auskunftenum 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_NA
im Code angezeigt wird, ist abnormal, undSTATE_IDLE
ist inaktiv, d. h. der Betriebszustand kann abgesichert werden.STATE_HOLD_LONG
ist der Zustand, in dem die positive Sicherungsposition gehalten wird.STATE_HOLD_SHORT
ist der Zustand, in dem die negative Sicherungsposition gehalten wird.
string replace(string s, const string from, const string& to)
toHex
inline unsigned char toHex(unsigned char x)
std::string urlencode(const std::string& str)
uint64_t _Time(string &s)
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
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).
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 dergetState
Die 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 dergetState
Es gibt auch einige Datenaktualisierungsvorgänge für das Diagramm.
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 derpayload
Dann ist ein wichtiger Schritt, die API-Schnittstellenfunktion der FMZ Quant-Plattform aufzurufen.Dial
DieDial
Hier 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 Wertqs
String derpayload
Parameter), um diese Funktion zu erreichen, müssen Sie Konfiguration in der Parameter-String derDial
function.
Der Beginn derDial
Funktionsparameter ist:
wss://real.okex.com:10442/ws/v3
Dies ist die Adresse der Websocket-Schnittstelle, auf die zugegriffen werden muss, und ist durch
|Parameter name|description|
|-|-|
|compress|compress is compression mode, OKEX websocket interface uses gzip_raw this way, so it is set to gzip_raw|
|mode|Mode is mode, optional dual, send and recv three kind. Dual is bidirectional, sending compressed data and receiving compressed data. Send is to send compressed data. Recv receives the compressed data and decompresses it locally.|
|reconnect|Reconnect is set to reconnect, reconnect=true to enable reconnection, no default is not reconnected.|
|payload|The payload is a subscription message that needs to be sent when ws is reconnected.|
After this setting, even if the websocket connection is disconnected, FMZ Quant trading platform's underlying system of the docker system will automatically reconnect and get the latest market data in time.
Grab every price fluctuation and quickly capture the right hedge.
- Position control
Position control is controlled using a ratio of hedge positions similar to the “Fibonaci” series.
für (int i = 0; i < AddMax + 1; i++) { // eine Datenstruktur konstruieren, die die Anzahl der Skalpings kontrolliert, ähnlich dem Verhältnis der Bofinac-Sequenz zur Anzahl der Absicherungen.
if (_addArr.size() < 2) { // Die ersten beiden hinzugefügten Positionen werden so geändert: doppelt die Anzahl der Absicherungen
_addArr.push_back((i+1)*OpenAmount);
- Ich weiß.
_addArr.push_back ((_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); // Die letzten beiden addierenden Positionen werden addiert, und die aktuelle Positionsgröße wird berechnet und in der Datenstruktur
It can be seen that the number of additional positions added each time is the sum of the last two positions.
Such position control can realize the larger the difference, the relative increase of the arbitrage hedge, and the dispersion of the position, so as to grasp the small position of the small price fluctuation, and the large price fluctuation position is appropriately increased.
- closing position: stop loss and take profit
Fixed stop loss spread and take profit spread.
When the position difference reaches the take profit position and the stop loss position, the take profit and stop loss are carried out.
- The designing of entering the market and leaving the market
The period of the parameter ```NPeriod``` control provides some dynamic control over the opening and closing position of the strategy.
- Strategy chart
The strategy automatically generates a spread K-line chart to mark relevant transaction information.
c++ strategy custom chart drawing operation is also very simple. You can see that in the constructor of the hedge class, we use the written chart configuration string ```_cfgStr``` to configure the chart object ```_c```, ```_c``` is the private component of the hedge class. When the private member is initialized, the ```chart``` object constructed by the FMZ Quant platform custom chart API interface function is called.
_cfgStr = R
Aufruf _c.update(_cfgStr); Verwenden Sie _cfgStr, um das Chart-Objekt zu konfigurieren.
call _c.reset(); um die Diagrammdaten zurückzusetzen.
When the strategy code needs to insert data into the chart, it also calls the member function of the ```_c``` object directly, or passes the reference of ```_c``` as a parameter, and then calls the object member function (method) of ```_c``` to update the chart data and insert operation.
E.g:
_c.add(chartIdx, {{
After placing the order, mark the K line chart.
As follows, when drawing a K line, a reference to the chart object ```_c``` is passed as a parameter when calling the member function ```feed``` of the ```BarFeeder``` class.
null feed ((Doppelpreis, Diagramm *c=nullptr, int DiagrammIdx=0)
That is, the formal parameter ```c``` of the ```feed``` function.
json-Punkt = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // Konstruieren Sie einen json-Datentyp
wenn (c!= nullptr) { // Der Zeiger des Chartobjekts nicht gleich dem Nullzeiger ist, tun Sie Folgendes.
wenn (newBar) { // Beurteilen, ob die neue Leiste angezeigt wird
c->add(chartIdx, Punkt); // ruft die Chartobjekt-Mitgliedsfunktion
Insert a new K-line Bar data into the chart by calling the ```add``` member function of the chart object ```_c```.
c->add ((DiagrammIdx, Punkt);
Diese Strategie dient ausschließlich Lern- und Kommunikationszwecken.
Strategieadresse:https://www.fmz.com/strategy/163447
Weitere interessante Strategien finden Sie auf der FMZ Quant-Plattform