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
compress=gzip_raw&mode=recv&reconnect=true&payload="+qs
sind 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.
Die Positionskontrolle erfolgt anhand eines Verhältnisses der Absicherungspositionen, das der
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.
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.
Periode des ParametersNPeriod
Die Strategie wird in der Regel durch eine dynamische Steuerung der Eröffnungs- und Schließposition der Strategie gesteuert.
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_cfgStr
Um das Chartobjekt zu konfigurieren_c
, _c
ist die private Komponente der Absicherungsklasse.chart
Objekt, 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._c
Objekt direkt, oder überträgt die Referenz von_c
als Parameter und ruft dann die Objekt-Mitgliedsfunktion (Methode) von_c
die 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_c
wird als Parameter übergeben, wenn die Mitgliederfunktion aufgerufen wirdfeed
derBarFeeder
class.
void feed(double price, chart *c=nullptr, int chartIdx=0)
Das heißt, der formale Parameterc
derfeed
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 dieadd
Funktion des Chartobjekts_c
.
c->add(chartIdx, point);
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