Ich bin hierBinance: Short-Over-Rising-Long-Over-Slumping-Multiwährungs-Hedging-StrategieGleichzeitig wurde auch eine Backtesting-Engine veröffentlicht. Der erste Bericht bestätigte die Wirksamkeit der Strategie anhand eines einstündigen K-Line-Backtests. Die tatsächliche Ruhezeit der öffentlichen Strategie beträgt jedoch 1 s, was eine sehr hochfrequente Strategie ist. Es ist offensichtlich unmöglich, durch Backtesting mit stündlicher K-Linie genaue Ergebnisse zu erhalten. Später hinzugefügtMinutenlinien-BacktestInfolgedessen ist der Backtesting-Gewinn stark gestiegen, es ist jedoch immer noch unmöglich zu bestimmen, welche Parameter in der Situation der zweiten Ebene verwendet werden sollten, und das Verständnis der gesamten Strategie ist nicht sehr klar. Der Hauptgrund ist der erhebliche Nachteil des K-Line-basierten Backtestings.
Zunächst einmal: Was ist die historische K-Linie? Die Daten einer K-Linie umfassen vier Preise: höchsten Eröffnungs- und niedrigsten Schlusskurs, zwei Startzeiten und ein Handelsvolumen von mehreren Hundert Dollar. Die meisten quantitativen Plattformen und Frameworks basieren auf K-Line-Backtesting und die quantitative FMZ-Plattform bietet auch Backtesting auf Tick-Level. Das K-Line-Backtesting ist sehr schnell und in den meisten Fällen problemlos, weist aber auch sehr gravierende Mängel auf. Insbesondere beim Backtesting von Strategien mit mehreren Varianten und Hochfrequenzstrategien ist es fast unmöglich, korrekte Schlussfolgerungen zu ziehen.
Das erste ist die Frage der Zeit. Die Zeit der höchsten und niedrigsten Preise der K-Liniendaten ist nicht angegeben, daher besteht keine Notwendigkeit, sie zu berücksichtigen. Aber die wichtigsten Eröffnungs- und Schlusspreise beginnen nicht mit der Eröffnung und Schließzeit. Selbst bei weniger beliebten Handelsprodukten wird oft länger als zehn Sekunden nicht gehandelt. Wenn wir Backtests von Multiproduktstrategien durchführen, gehen wir oft davon aus, dass ihre Eröffnungs- und Schlusskurse gleichzeitig sind. Dies ist auch die Grundlage für das Backtesting des Schlusskurses.
Stellen Sie sich vor, Sie verwenden die Minutenlinie, um die Arbitrage zweier Sorten zu testen. Ihr Preisunterschied beträgt normalerweise 10 Yuan. Nun stellt sich heraus, dass um 10:01 der Schlusskurs von Kontrakt A 100 und der von Kontrakt B 112 beträgt. Der Preisunterschied beträgt 12 Yuan. Die Strategie beginnt also mit der Absicherung. In diesem Moment kehrte die Preisdifferenz zurück und die Strategie erzielte einen Gewinn von 2 Yuan.
Die tatsächliche Situation kann sein, dass Vertrag A um 10:00:45 eine Transaktion von 100 Yuan generierte und danach keine Transaktion mehr stattfand. Um 10:00:58 generierte Vertrag B eine Transaktion von 112 Yuan. Um 10:01, beide Preise existieren nicht, wie hoch ist der Marktpreis zu diesem Zeitpunkt und wie viel Preisdifferenz kann durch Absicherung erzielt werden? Es gibt keine Möglichkeit, das herauszufinden. Ein mögliches Szenario ist: Um 10:00:58 Uhr beträgt der Geld-Brief-Kurs von Kontrakt A 101,9-102,1, und es gibt überhaupt keinen Preisunterschied von 2 Yuan. Dies würde unsere Strategieoptimierung stark in die Irre führen.
Das zweite Problem ist das Matching. Beim echten Matching stehen Preis und Zeit im Vordergrund. Wenn der Käufer den Briefkurs überschreitet, wird die Transaktion im Allgemeinen direkt zum Briefkurs abgeschlossen. Andernfalls wird er in das Auftragsbuch eingetragen und wartet. Die K-Line-Daten verfügen offensichtlich nicht über einen Kauf- und Verkaufspreis und es ist unmöglich, eine Übereinstimmung auf detaillierter Ebene zu simulieren.
Schließlich gibt es noch die Auswirkungen der Strategie selbst auf den Markt. Wenn es sich um einen Backtest mit kleinem Kapital handelt, werden die Auswirkungen nicht signifikant sein. Wenn das Handelsvolumen jedoch einen großen Anteil ausmacht, wird dies Auswirkungen auf den Markt haben. Nicht nur wird der Preisrutsch groß sein, wenn die Transaktion sofort ausgeführt wird, sondern wenn Ihre Kauforder durch Backtesting ausgeführt wird, wird sie tatsächlich die Transaktionen anderer Händler, die ursprünglich kaufen wollten, vorwegnehmen, und der Schmetterlingseffekt wird Auswirkungen auf die Markt. Dieser Einfluss lässt sich nicht quantifizieren und wir können aus Erfahrung nur sagen, dass Hochfrequenzhandel nur für kleine Fonds in Frage kommt.
FMZ bietet Backtesting in Echtzeit, mit dem echte historische Tiefen von 20 Ebenen, Echtzeit-Ticks der zweiten Ebene, Daten zu einzelnen Transaktionen und andere Daten abgerufen werden können.Echtzeit-Wiedergabefunktion. Die Menge der Backtesting-Daten ist extrem groß und die Geschwindigkeit sehr langsam, normalerweise nur zwei Tage. Für Strategien mit relativ hoher Frequenz oder Strategien, die eine strenge zeitliche Beurteilung erfordern, ist ein Backtesting in Echtzeit erforderlich. Die von FMZ erfassten Transaktionspaare und Zeiträume sind zwar nicht lang, umfassen jedoch mehr als 70 Milliarden historische Daten. Der aktuelle Matching-Mechanismus besteht darin, dass, wenn die Kauforder größer als die Verkaufsorder ist, diese unabhängig vom Volumen sofort vollständig abgeglichen wird; wenn sie kleiner als die Verkaufsorder ist, wird sie in die Matching-Warteschlange aufgenommen. Ein solcher Backtesting-Mechanismus löst die ersten beiden Probleme des K-Line-Backtestings, kann das letzte Problem jedoch immer noch nicht lösen. Und weil die Datenmenge so groß ist, sind die Backtesting-Geschwindigkeit und der Zeitrahmen begrenzt.
Es gibt zu wenig K-Line-Informationen und die Tiefe kann falsch sein, aber es gibt eine Art von Daten, die die wahre Transaktionsabsicht des Marktes widerspiegeln und die authentischste Transaktionshistorie widerspiegeln - das heißt, Transaktion für Transaktion. In diesem Artikel wird ein auf dem Auftragsfluss basierendes Hochfrequenz-Backtesting-System vorgeschlagen, das die Datenmenge für das Echtzeit-Backtesting erheblich reduziert und die Auswirkungen des Handelsvolumens auf den Markt bis zu einem gewissen Grad simuliert.
Ich habe die Transaktionsaufzeichnungen des unbefristeten Binance XTZ-Vertrags der letzten 5 Tage heruntergeladen (Download-Adresse: https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv). Als nicht sehr beliebtes Produkt gibt es 213.000 Transaktionen in Gesamtdaten, schauen wir uns zunächst die Zusammensetzung der Daten an:
[['XTZ', 1590981301905, 2.905, 0.4, 'False\n'],
['XTZ', 1590981303044, 2.903, 3.6, 'True\n'],
['XTZ', 1590981303309, 2.903, 3.7, 'True\n'],
['XTZ', 1590981303738, 2.903, 238.1, 'True\n'],
['XTZ', 1590981303892, 2.904, 0.1, 'False\n'],
['XTZ', 1590981305250, 2.904, 0.1, 'False\n'],
['XTZ', 1590981305643, 2.903, 197.3, 'True\n'],
Die Daten sind eine zweidimensionale Liste, sortiert nach Transaktionszeit. Die spezifischen Bedeutungen sind: Produktname, Transaktionspreis, Transaktionszeitstempel, Transaktionsmenge und ob es sich um eine aktive Verkaufsauftragstransaktion handelt. Es gibt Käufer und Verkäufer, und jede Transaktion umfasst einen Käufer und einen Verkäufer. Wenn der Käufer ein Market Maker und der Verkäufer ein Taker ist, sind die letzten Daten True.
Erstens können die Kauf- und Verkaufspreise auf dem Markt anhand der Transaktionsrichtung recht genau abgeleitet werden. Wenn es sich um eine aktive Verkaufsorder handelt, ist der Kaufpreis zu diesem Zeitpunkt der Transaktionspreis. Wenn es sich um eine aktive Kauforder handelt, ist der Verkaufspreis ist der Transaktionspreis. Wenn eine neue Transaktion stattfindet, wird das neue Angebot aktualisiert. Wenn es nicht aktualisiert wird, bleibt das vorherige Ergebnis erhalten. Daraus lässt sich leicht folgern, dass im letzten Moment der obigen Daten der Kaufpreis 2,903 und der Verkaufspreis 2,904 betrug.
Gemäß dem Auftragsfluss kann das Matching folgendermaßen erfolgen: Am Beispiel einer Kauforder ist der Preis der Preis, die Auftragsmenge ist der Betrag und die Kauf- und Verkaufsorders sind jeweils Gebot und Brief. Wenn der Preis niedriger als der Briefkurs und höher als der Geldkurs ist, wird er zunächst als Hersteller beurteilt und kann zuerst mit der Transaktion abgeglichen werden. Dann werden innerhalb der Existenzzeit der Bestellung alle Transaktionen mit einem Transaktionspreis niedriger als oder gleich zum Preis wird mit dieser Bestellung abgeglichen (wenn der Preis niedriger als der Briefkurs ist, ist der Transaktionspreis höher als der Geldkurs). Wenn der Geldkurs gleich oder gleich dem Geldkurs ist, kann er nicht zuerst gehandelt werden. Alle Bestellungen mit einem niedrigeren Transaktionspreis als dem Preis wird dieser Auftrag abgeglichen. Der Abgleichspreis ist der Preis und das Transaktionsvolumen ist das Transaktionsvolumen jeder Transaktion, bis der Auftrag vollständig ausgeführt oder storniert wird. Wenn der Preis höher ist als der Briefkurs, wird er als Abnehmer betrachtet. Danach werden alle Transaktionen mit einem Transaktionspreis, der niedriger oder gleich dem Preis innerhalb der Existenzzeit der Bestellung ist, mit dieser Bestellung abgeglichen, und der entsprechende Preis wird sei der Transaktionspreis der Transaktion. Die Unterscheidung zwischen Makern und Takern liegt darin begründet, dass Börsen grundsätzlich dazu ermutigen, Aufträge zu erteilen und vergünstigte Transaktionsgebühren anbieten. Bei Hochfrequenzstrategien muss diese Unterscheidung berücksichtigt werden.
Bei dieser Art der Zuordnung ist ein Problem leicht zu erkennen. Wenn es sich bei der Order um einen Taker handelt, kann sie tatsächlich sofort ausgeführt werden, anstatt darauf zu warten, dass neue Orders dazu passen. Erstens haben wir das Volumen der ausstehenden Aufträge nicht berücksichtigt. Selbst wenn es Daten gäbe, würde eine direkte Beurteilung der Transaktion die Tiefe verändern und den Markt beeinflussen. Das Matching auf Grundlage neuer Aufträge entspricht dem Ersetzen der tatsächlichen Aufträge in der Historie durch Ihre Aufträge. In jedem Fall wird dadurch die Grenze des eigenen Handelsvolumens des Marktes nicht überschritten und der endgültige Gewinn wird den vom Markt erzielten Höchstgewinn nicht überschreiten. Einige Matching-Mechanismen wirken sich auch auf das Transaktionsvolumen von Aufträgen aus, was wiederum die Rendite der Strategie beeinflusst und die Strategiekapazität quantitativ widerspiegelt. Es wird keinen traditionellen Backtest geben, bei dem sich der Gewinn verdoppelt, wenn sich der eingesetzte Geldbetrag verdoppelt.
Es gibt einige kleine Details. Wenn der Kaufpreis der Bestellung dem Kaufpreis entspricht, besteht tatsächlich immer noch eine gewisse Wahrscheinlichkeit, dass sie zum Kaufpreis ausgeführt wird. Es ist notwendig, die Priorität der ausstehenden Bestellung und die Transaktionswahrscheinlichkeit usw. Es ist relativ kompliziert und wird hier nicht berücksichtigt.
Die Tauschobjekte können auf die Einführung am Anfang verweisen, diese sind grundsätzlich unverändert, es kommt lediglich die Differenz zwischen Maker- und Taker-Gebühren hinzu und die Backtesting-Geschwindigkeit wird optimiert. Im Folgenden wird hauptsächlich der passende Code vorgestellt.
symbol = 'XTZ'
loop_time = 0
intervel = 1000 #策略的休眠时间为1000ms
init_price = data[0][2] #初始价格
e = Exchange([symbol],initial_balance=1000000,maker_fee=maker_fee,taker_fee=taker_fee,log='') #初始化交易所
depth = {'ask':data[0][2], 'bid':data[0][2]} #深度
order = {'buy':{'price':0,'amount':0,'maker':False,'priority':False,'id':0},
'sell':{'price':0,'amount':0,'maker':False,'priority':False,'id':0}} #订单
for tick in data:
price = int(tick[2]/tick_sizes[symbol])*tick_sizes[symbol] #成交价格
trade_amount = tick[3] #成交数量
time_stamp = tick[1] #成交时间戳
if tick[4] == 'False\n':
depth['ask'] = price
else:
depth['bid'] = price
if depth['bid'] < order['buy']['price']:
order['buy']['priority'] = True
if depth['ask'] > order['sell']['price']:
order['sell']['priority'] = True
if price > order['buy']['price']:
order['buy']['maker'] = True
if price < order['sell']['price']:
order['sell']['maker'] = True
#订单网络延时也可以作为撮合条件之一,这里没考虑
cond1 = order['buy']['priority'] and order['buy']['price'] >= price and order['buy']['amount'] > 0
cond2 = not order['buy']['priority'] and order['buy']['price'] > price and order['buy']['amount'] > 0
cond3 = order['sell']['priority'] and order['sell']['price'] <= price and order['sell']['amount'] > 0
cond4 = not order['sell']['priority'] and order['sell']['price'] < price and order['sell']['amount'] > 0
if cond1 or cond2:
buy_price = order['buy']['price'] if order['buy']['maker'] else price
e.Buy(symbol, buy_price, min(order['buy']['amount'],trade_amount), order['buy']['id'], order['buy']['maker'])
order['buy']['amount'] -= min(order['buy']['amount'],trade_amount)
e.Update(time_stamp,[symbol],{symbol:price})
if cond3 or cond4:
sell_price = order['sell']['price'] if order['sell']['maker'] else price
e.Sell(symbol, sell_price, min(order['sell']['amount'],trade_amount), order['sell']['id'], order['sell']['maker'])
order['sell']['amount'] -= min(order['sell']['amount'],trade_amount)
e.Update(time_stamp,[symbol],{symbol:price})
if time_stamp - loop_time > intervel:
order = get_order(e,depth,order) #交易逻辑,这里未给出
loop_time += int((time_stamp - loop_time)/intervel)*intervel
Einige Einzelheiten sind zu beachten:
Schließlich sind wir bei der eigentlichen Backtesting-Phase angelangt. Wir werden eine der klassischsten Grid-Strategien einem Backtest unterziehen, um zu sehen, ob sie die erwarteten Ergebnisse erzielt. Das Prinzip der Strategie besteht darin, dass wir jedes Mal, wenn der Preis um 1 % steigt, eine Short-Order mit einem bestimmten Wert halten (oder umgekehrt eine Long-Order halten), die Kauf- und Verkaufsaufträge berechnen und diese im Voraus platzieren. Der Code wird nicht freigegeben. Kapseln Sie den gesamten Code inGrid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
In der Funktion lauten die Parameter: Handelspaar, Preisabweichung von 1 % des Haltewerts, Auftragsdichte von 0,3 %, Ruheintervall in ms, Gebühr für Auftragsersteller und Gebühr für Auftragsempfänger.
In den letzten fünf Tagen befand sich der XTZ-Markt in einer volatilen Phase, die für das Netz sehr günstig ist.
Wir führen zunächst einen Backtest durch, um die Auswirkungen unterschiedlicher Positionsgrößen auf die Rendite zu ermitteln. Die mit dem traditionellen Backtesting-Mechanismus gemessenen Renditen steigen mit Sicherheit proportional zur Zunahme der Positionen.
e1 = Grid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e1.account['USDT'])
e2 = Grid('XTZ',1000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e2.account['USDT'])
e3 = Grid('XTZ',10000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e3.account['USDT'])
e4 = Grid('XTZ',100000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e4.account['USDT'])
Insgesamt wurden vier Gruppen mit Haltewerten von 100, 1000, 10000 bzw. 100000 einem Backtesting unterzogen. Die gesamte Backtesting-Zeit betrug 1,3 Sekunden. Die Ergebnisse sind wie folgt:
{'realised_profit': 28.470993031132966, 'margin': 0.7982662957624465, 'unrealised_profit': 0.0104554474048441, 'total': 10000028.481448, 'leverage': 0.0, 'fee': -0.3430967859046398, 'maker_fee': -0.36980249726699727, 'taker_fee': 0.026705711362357405}
{'realised_profit': 275.63148945320177, 'margin': 14.346335829979132, 'unrealised_profit': 4.4382117331794045e-14, 'total': 10000275.631489, 'leverage': 0.0, 'fee': -3.3102045933457784, 'maker_fee': -3.5800688964477048, 'taker_fee': 0.2698643031019274}
{'realised_profit': 2693.8701498889504, 'margin': 67.70120400534114, 'unrealised_profit': 0.5735269329348516, 'total': 10002694.443677, 'leverage': 0.0001, 'fee': -33.984021415250744, 'maker_fee': -34.879233866850974, 'taker_fee': 0.8952124516001403}
{'realised_profit': 22610.231198585603, 'margin': 983.3853688758861, 'unrealised_profit': -20.529965947304365, 'total': 10022589.701233, 'leverage': 0.002, 'fee': -200.87094000385412, 'maker_fee': -261.5849078470078, 'taker_fee': 60.71396784315319}
Es ist ersichtlich, dass die endgültigen realisierten Gewinne 28,4 %, 27,5 %, 26,9 % bzw. 22,6 % des Positionswerts betrugen. Dies entspricht auch der tatsächlichen Situation. Je höher der Wert der Position, desto höher der Wert der ausstehenden Bestellung, desto wahrscheinlicher ist es, dass eine Teiltransaktion stattfindet und der endgültig realisierte Gewinn im Verhältnis zum ausstehendes Auftragsvolumen. Die folgende Abbildung vergleicht die relativen Renditen von Beteiligungen mit Werten von 100 bzw. 10.000:
Wir können auch die Auswirkungen verschiedener Parameter auf die Backtesting-Renditen, wie etwa Auftragsdichte, Ruhezeit, Bearbeitungsgebühren usw., rückwirkend testen. Nehmen Sie die Ruhezeit als Beispiel, ändern Sie sie auf 100 ms und vergleichen Sie sie mit der Ruhezeit von 1000 ms, um die Vorteile zu sehen. Die Backtest-Ergebnisse lauten wie folgt:
{'realised_profit': 29.079440803790423, 'margin': 0.7982662957624695, 'unrealised_profit': 0.0104554474048441, 'total': 10000029.089896, 'leverage': 0.0, 'fee': -0.3703702128662524, 'maker_fee': -0.37938946377435134, 'taker_fee': 0.009019250908098965}
Der Gewinn ist leicht gestiegen. Dies liegt daran, dass die Strategie nur einen Satz von Aufträgen platziert. Einige Aufträge können nicht von den schwankenden Preisen profitieren, da sie nicht rechtzeitig geändert werden können. Die Verkürzung der Ruhezeit hat dieses Problem verbessert. Dies verdeutlicht auch, wie wichtig die Platzierung mehrerer Auftragsgruppen in einer Grid-Strategie ist.
Dieses Papier schlägt auf innovative Weise ein neues Backtesting-System auf der Grundlage des Auftragsflusses vor, das die Übereinstimmungsbedingungen von ausstehenden Aufträgen, angenommenen Aufträgen, Teiltransaktionen, Verzögerungen usw. teilweise simulieren und teilweise die Auswirkungen von Strategiefonds auf die Rendite widerspiegeln kann. Es enthält wichtige Referenzen Wert für Absicherungsstrategien und hochpräzises Backtesting weist die Richtung zur Optimierung der Strategieparameter. Dies wurde auch durch langfristigen tatsächlichen Handel bestätigt. Darüber hinaus lässt sich die für das Backtesting erforderliche Datenmenge besser kontrollieren und die Backtesting-Geschwindigkeit ist ebenfalls sehr hoch.