Als ich schriebErforschung von Binance Futures Multi-Währung Hedging Strategie, Ich habe auch eine Backtest-Engine veröffentlicht. Und der erste Bericht basierte auf dem einstündigen K-Line-Backtest, der die Wirksamkeit der Strategie bestätigte. Aber die Schlafzeit der tatsächlichen Open-Source-Strategie beträgt
Zunächst einmal, was ist die historische K-Linie? Eine K-Linie Daten enthält vier Preise von hoch, offen, niedrig, schließen, die ersten beiden
Der erste ist die Frage der Zeit. Die Zeit des höchsten Preises und des niedrigsten Preises der K-Liniendaten ist nicht angegeben und muss nicht berücksichtigt werden, aber die wichtigsten Eröffnungs- und Schlusskurs sind nicht die Eröffnungs- und Schlusskurszeit. Selbst die weniger beliebten Handelsvarianten haben oft keinen Handel für mehr als zehn Sekunden, und wenn wir die Multi-Variety-Strategie zurücktesten, gehen wir oft davon aus, dass ihr Eröffnungs- und Schlusskurs gleich sind, was auch auf dem Rücktest des Schlusskurses basiert.
Stellen Sie sich vor, Sie verwenden die Minute-Level-K-Linie, um die Arbitrage von zwei Varianten zu testen. Die Differenz zwischen ihnen beträgt normalerweise 10 Yuan ((oder Dollar). Jetzt, um 10:01, ist der Schlusskurs von Vertrag A 100, Vertrag B ist 112 und die Differenz beträgt 12 Yuan. Also beginnt die Strategie, sich abzusichern. Zu einem bestimmten Zeitpunkt kehrte die Preisdifferenz zurück und die Strategie erzielte einen Rückgewinn von 2 Yuan.
Aber die tatsächliche Situation kann sein, dass um 10:00:45 Vertrag A eine Transaktion von 100 Yuan erzeugt hat, danach gab es keine Transaktion, Vertrag B hatte eine Transaktion von 112 Yuan um 10:00:58, um 10:01:00 Beide Preise existieren nicht. Was ist der Marktpreis zu dieser Zeit, und wie viel kann die Absicherungsaktion bekommen? Ich kann nicht wissen. Eine mögliche Situation ist: um 10:00:58, der 101.9
zu102.1
Das wird unsere Strategie-Optimierung stark irreführen.
Das zweite ist das Matchmaking-Problem. Das echte Matchmaking ist Preispriorität und Zeitpriorität. Wenn der Käufer den
Der letzte ist die Wirkung der Strategie selbst auf den Markt. Wenn es sich um einen Backtest von kleinen Beträgen handelt, ist die Wirkung nicht groß. Aber wenn das Transaktionsvolumen groß ist, wird es Auswirkungen auf den Markt haben. Nicht nur wird der Preisrutsch groß sein, wenn Sie einen großen Volumen-Auftrag platzieren, wenn Sie einen langen Auftrag kaufen, der ausgeführt wird, diese Art von Aktion tatsächlich die Aufträge anderer Händler erfassen, die ursprünglich kaufen wollten, der
FMZ bietet einen echten Backtest, der echte historische Daten ermitteln kann.20 layer depth price
, in Echtzeit zweite EbeneTicks
, Each Individual Transaction
Auf der Grundlage dieser Funktionen hat FMZ eine Echtzeit-Transaktionswiedergabefunktion erstellt.
Diese Art von Backtest-Daten ist sehr groß und die Backtest-Geschwindigkeit ist auch sehr langsam, kann im Allgemeinen nur für zwei Tage zurücktesten. Für relativ hohe Frequenz- oder zeitkritische Strategien ist ein echter Markt-Level-Backtest notwendig. Die von FMZ gesammelten Handelspare und Handelszeiten sind nicht sehr lang, aber es gibt immer noch mehr als 70 Milliarden historische Daten.
Der aktuelle Matchmaking-Mechanismus besteht darin, dass, wenn die Bestellung größer als
Es gibt zu wenige Informationen in der K-Linie, und die Preistiefe kann auch eine falsche Tiefe sein, aber es gibt eine Art von Daten, die die reale Transaktionsbereitschaft des Marktes sind, die die realste Transaktionsgeschichte widerspiegelt, dhEach Individual Transaction
In diesem Artikel wird ein auf dem Auftragsfluss basierendes Hochfrequenz-Backtestsystem vorgeschlagen, das das Volumen der realen Markt-Backtestdaten erheblich reduzieren und bis zu einem gewissen Grad die Auswirkungen des Handelsvolumens auf den Markt simulieren wird.
Ich habe die Transaktion der letzten 5 Tage heruntergeladen Binance XTZ perpetual contract (Download-Adresse:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv), als nicht beliebte Sorte, hat es insgesamt 213000 Transaktionsdaten, 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, die in chronologischer Reihenfolge sortiert ist. Die spezifischen Bedeutungen sind wie folgt: Sortenname, Transaktionspreis, Transaktionszeitstempel, Transaktionsmenge, ob es sich um eine Verkaufsbestellung aktive Transaktion handelt. Es gibt Kauf- und Verkaufsseite, und jede Transaktion umfasst Käufer und Verkäufer.Maker
und der Verkäufer ist ein aktiverTaker
, sind die letzten DatenTrue
.
Zunächst einmal können Sie nach der Richtung der Transaktion ziemlich genau über die
Nach dem Auftragsfluss kann er folgendermaßen abgeglichen werden: Nehmen wir zum Beispiel einen Kaufbefehl, der Preis istprice
, ist die Bestellmengeamount
, dann kaufen und verkaufen 1 zu diesem Zeitpunkt sindbid
undask
Siehe auchprice
ist niedriger alsask
und höher alsbid
, dann wird es alsmaker
Erstens, und die Priorität kann abgestimmt werden, um einen Deal zu machen, dann alle Geschäfte mit einem Transaktionspreis niedriger als oder gleich demprice
Während der Bestellzeit wird die Bestellzeit mit dieser Bestellung abgeglichen (wennprice
ist kleiner oder gleichbid
, wird der Transaktion keine Priorität eingeräumt.price
sind mit dieser Reihenfolge übereinstimmen.)
Der entsprechende Preis beträgtprice
, und das Volumen ist das Transaktionsvolumen vonEach Individual Transaction
, bis die Bestellung vollständig abgeschlossen ist oder bis die Bestellung storniert wird.ask
, wird es alstaker
Danach, während der Zeit, in der der Auftrag besteht, werden alle Geschäfte mit einem Transaktionspreis, der kleiner oder gleichprice
Der entsprechende Preis ist der Transaktionspreis derEach Individual Transaction
Die Unterscheidung zwischenmaker
undtaker
Bei den Hochfrequenz-Strategien muss dieser Unterschied berücksichtigt werden.
Es ist leicht, ein Problem mit dieser Art der Übereinstimmung zu sehen.taker
, die tatsächliche Situation ist, dass es sofort ausgeführt werden kann, anstatt auf eine neue Bestellung zu warten, um mit ihm abgestimmt werden.
Der Mechanismus der Abgleichung beeinflusst auch das Auftragsvolumen, was wiederum die Einnahmen der Strategie beeinflusst und die Kapazität der Strategie quantitativ widerspiegelt. Es wird keine traditionelle Rückprüfung geben, wenn sich die Geldmenge verdoppelt und der Gewinn verdoppelt.
Wenn der Kaufpreis der Bestellung gleich
Austauschobjekte können sich auf die Einführung am Anfang beziehen, grundsätzlich unverändert, nur den Unterschied zwischenmaker
undtaker
Die folgenden Artikel werden vor allem den Matching-Code vorstellen.
symbol = 'XTZ'
loop_time = 0
intervel = 1000 # The sleep time of the strategy is 1000ms
init_price = data[0][2] # Initial price
e = Exchange([symbol],initial_balance=1000000,maker_fee=maker_fee,taker_fee=taker_fee,log='') # Initialize the exchange
depth = {'ask':data[0][2], 'bid':data[0][2]} # depth
order = {'buy':{'price':0,'amount':0,'maker':False,'priority':False,'id':0},
'sell':{'price':0,'amount':0,'maker':False,'priority':False,'id':0}} # order
for tick in data:
price = int(tick[2]/tick_sizes[symbol])*tick_sizes[symbol] # executed price
trade_amount = tick[3] # executed volume
time_stamp = tick[1] # executed timestamp
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
# Order network delay can also be used as one of the matching conditions, not considered here
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) # Trading logic, not given here
loop_time += int((time_stamp - loop_time)/intervel)*intervel
Ein paar Details zu beachten:
Wenn es eine neue Transaktion gibt, müssen wir zuerst die Bestellung anpassen und dann die Bestellung nach dem letzten Preis platzieren.
Jeder Auftrag hat zwei Attribute: makermaker
, und wenn der Kaufpreis größer als Priority matching
, priority
bestimmt, ob der Preis dem Kaufpreis entspricht oder nicht, und der Maker bestimmt die Transaktionsgebühr.
Diemaker
undpriority
Wenn ein großer Kauf stattgefunden hat und die Marktkapazität übersteigt, wird das verbleibende Volumen, wenn der Preis größer ist als der Kaufpreis, dasmaker
.
Strategieinterval
Der Präsident. - Nach der Tagesordnung folgt die Aussprache über den Bericht (Dok.
Letztendlich ist es die eigentliche Backtest-Phase. Lassen Sie uns eine der klassischsten Gitterstrategien hier zurücktesten, um zu sehen, ob wir die erwarteten Ergebnisse erzielen können. Das Prinzip der Strategie ist, dass jedes Mal, wenn der Preis um 1% steigt, halten wir eine kurze Bestellung eines bestimmten Wertes (umgekehrt, halten wir eine lange Bestellung), berechnen die Kauf- und Verkaufsanordnung im Voraus. Ich werde Ihnen nicht den Quellcode zeigen. Sie sind alle in dieGrid('XTZ', 100, 0.3, 1000, maker_fee=-0.00002, taker_fee=0.0003)
Funktion, die Parameter sind: Handelspaar, Preis abweichen von dem Holding-Wert von 1%, ausstehende Orderdichte ist 0,3%, Sleep-Intervallms
, ausstehende Auftragsgebühren und ausgeführte Auftragsgebühren.
Der Marktpreis von XTZ
Wir prüfen zunächst den Effekt verschiedener Holdingpositionen auf die Gewinnrendite.
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 einem Wert von 100, 1000, 10000, 100 000 Positionen und einer Gesamtzeit von 1,3 Sekunden zurück getestet.
{'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 kann gesehen werden, dass die endgültigen realisierten Gewinne 28,4%, 27,5%, 26,9% und 22,6% des Haltepositionwerts betragen. Dies entspricht auch der tatsächlichen Situation. Je größer der Wert der Halteposition, desto größer der Wert der ausstehenden Order, desto wahrscheinlicher wird eine teilweise Transaktion stattfinden, und je kleiner der endgültige realisierte Gewinn im Verhältnis zum Betrag der ausstehenden Order. Die folgende Grafik ist ein Vergleich der relativen Renditen des Positionswerts von 100 bzw. 10000:
Wir können auch den Einfluss verschiedener Parameter auf die Umsätze von Backtests überprüfen, wie z. B. Warteaufträge, Schlafzeit, Transaktionsgebühren usw. Nehmen Sie die Schlafzeit als Beispiel, ändern Sie sie auf 100 ms und vergleichen Sie die Schlafzeit mit 1000 ms, um die Gewinnrendite zu sehen. Die Ergebnisse des Backtests sind 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}
Dies zeigt auch, wie wichtig die Netzstrategie für die Platzierung mehrerer Bestellungen ist.
Dieser Artikel schlägt innovativ ein neues Backtest-System auf Basis des Orderflusses vor, das teilweise die Übereinstimmung von ausstehenden Aufträgen, ausgeführten Aufträgen, teilweise ausgeführten Aufträgen, Verzögerungen usw. simulieren kann und teilweise die Auswirkungen der Strategiefondsmenge auf den Umsatz widerspiegelt. Für Hochfrequenz- und Hedging-Strategien hat es einen wichtigen Referenzwert.