Hochfrequenz-Backtest-System basierend auf jeder Transaktion und den Mängeln des K-Line-Backtests

Schriftsteller:Gutes, Erstellt: 2020-06-16 10:30:19, Aktualisiert: 2023-11-01 20:26:21

img

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 1 Sekunde, was eine ziemlich hohe Frequenzstrategie ist. Offensichtlich kann die Verwendung des stündlichen K-Line-Backtests keine genauen Ergebnisse liefern. Später wurden die Ergebnisse des Backtests der Minute-Level-K-Line hinzugefügt, und die Einnahmen aus dem Backtest haben sich stark verbessert, aber es ist immer noch unmöglich zu bestimmen, welche Parameter im Falle von Sekunden-Level verwendet werden sollten, und das Verständnis der gesamten Strategie ist nicht sehr klar. Der Hauptgrund ist der wichtige Nachteil des auf K-Line basierenden Backtests.

Probleme auf der Grundlage von K-Line-Backtest

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 times und das Volumen des Intervalls. Die meisten Quantifizierung Plattformen und Frameworks basieren auf K-Line-Backtest, und die FMZ-Plattform bietet auch tick-level-Backtest.

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 Buy 1 und Sell 1 pending order Preis von Vertrag A ist101.9zu102.1Das 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 Sell 1 Preis überschreitet, handelt er normalerweise direkt zum Sell 1 Preis, andernfalls geht er in das ausstehende Auftragsbuch und wartet. Die K-Zeilendaten haben offensichtlich keinen Buy 1 und Sell 1 Preis, es ist unmöglich, das Detailpreisniveau zu simulieren.

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 Butterfly-Effekt wird Auswirkungen auf den Markt haben. Dieser Effekt kann nicht quantifiziert werden. Wir können nur aus Erfahrung sagen, dass Hochfrequenzhandel nur kleine Mittel aufnehmen kann.

Backtest auf Basis von Echtzeittiefe und Tick

FMZ bietet einen echten Backtest, der echte historische Daten ermitteln kann.20 layer depth price, in Echtzeit zweite EbeneTicks, Each Individual TransactionAuf 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 Sell 1 ist, sie sofort vollständig abgestimmt wird, ohne auf den Betrag zu achten, und wenn sie kleiner als Sell 1 ist, sie in die Match-Warteschlange eintritt, um zu warten.

img

Backtest-Mechanismus auf Basis des Auftrags-für-Auftrags-Transaktionsflusses

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 TransactionIn 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.Makerund 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 Buy 1 und Sell 1 auf dem Markt spekulieren. Wenn es sich um eine aktive Verkaufsorder handelt, ist der Buy 1 Preis zu diesem Zeitpunkt der Transaktionspreis, wenn es sich um eine aktive Kauforder handelt, wird der Sell 1 Preis der Transaktionspreis sein. Wenn es eine neue Transaktion gibt, wird dann der gesamte Preis erneuert und aktualisiert. Das letzte Ergebnis wird beibehalten, wenn es keine Erneuerung und Aktualisierung gibt. Es ist einfach, den letzten Moment der oben genannten Daten einzuführen, der Buy 1 Preis beträgt 2.903, und der Sell 1 beträgt 2.904.

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 sindbidundaskSiehe auchpriceist niedriger alsaskund höher alsbid, dann wird es alsmakerErstens, und die Priorität kann abgestimmt werden, um einen Deal zu machen, dann alle Geschäfte mit einem Transaktionspreis niedriger als oder gleich dempriceWährend der Bestellzeit wird die Bestellzeit mit dieser Bestellung abgeglichen (wennpriceist kleiner oder gleichbid, wird der Transaktion keine Priorität eingeräumt.pricesind 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 alstakerDanach, während der Zeit, in der der Auftrag besteht, werden alle Geschäfte mit einem Transaktionspreis, der kleiner oder gleichpriceDer entsprechende Preis ist der Transaktionspreis derEach Individual TransactionDie Unterscheidung zwischenmakerundtakerBei 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 Buy 1 ist, besteht immer noch eine gewisse Wahrscheinlichkeit, dass der Kaufpreis durch Buy 1 erreicht wird, wird diese Art von Situation hier nicht berücksichtigt.

Entsprechender Code

Austauschobjekte können sich auf die Einführung am Anfang beziehen, grundsätzlich unverändert, nur den Unterschied zwischenmakerundtakerDie 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: makerob es sich um einen Maker handelt, PrioritätPrioritätsgleichstellung, wenn der Kaufbefehl zum Beispiel bei einem Kaufpreis unter Sell 1 liegt, wird er alsmaker, und wenn der Kaufpreis größer als Kauf 1 ist, wird er alsPriority matching, prioritybestimmt, ob der Preis dem Kaufpreis entspricht oder nicht, und der Maker bestimmt die Transaktionsgebühr.

  • DiemakerundpriorityWenn 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.

  • StrategieintervalDer Präsident. - Nach der Tagesordnung folgt die Aussprache über den Bericht (Dok.

Backtest der Netzstrategie

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 war in den letzten 5 Tagen in Schock, was für Netze sehr geeignet ist.

img

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:

img

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.

Zusammenfassend

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.


Verwandt

Mehr