3
konzentrieren Sie sich auf
1444
Anhänger

Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Erstellt in: 2023-12-08 17:00:38, aktualisiert am: 2023-12-14 17:07:42
comments   1
hits   2862

Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Die Perpetual-Grid-Strategie ist eine beliebte klassische Strategie auf der Plattform. Im Vergleich zum Spot-Grid müssen keine Münzen gehalten werden und es kann ein Hebel hinzugefügt werden, was viel praktischer ist als beim Spot-Grid. Da es jedoch unmöglich ist, Backtests direkt auf der Inventor Quantitative Platform durchzuführen, ist dies nicht förderlich für das Screening von Währungen und die Bestimmung der Parameteroptimierung. In diesem Artikel wird der vollständige Python-Backtesting-Prozess vorgestellt, einschließlich Datenerfassung, Backtesting-Framework, Backtesting-Funktion, Parameteroptimierung, usw. Sie können es selbst im Juypter-Notizbuch ausprobieren.

Datenerfassung

Im Allgemeinen reichen K-Line-Daten aus. Aus Gründen der Genauigkeit ist es besser, wenn die K-Line-Periode kleiner ist. Allerdings sollten Backtest-Zeit und Datenvolumen ausgewogen sein. Dieser Artikel verwendet 5-Minuten-Daten aus den letzten zwei Jahren für das Backtesting. . Das endgültige Datenvolumen übersteigt 200.000 Zeilen. Wählen Sie DYDX. Natürlich können Sie die jeweilige Währung und den K-Line-Zeitraum entsprechend Ihren eigenen Interessen auswählen.

import requests
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests, zipfile, io
%matplotlib inline

def GetKlines(symbol='BTC',start='2020-8-10',end='2021-8-10',period='1h'):
    Klines = []
    start_time = int(time.mktime(datetime.strptime(start, "%Y-%m-%d").timetuple()))*1000
    end_time = int(time.mktime(datetime.strptime(end, "%Y-%m-%d").timetuple()))*1000
    while start_time < end_time:
        res = requests.get('https://fapi.binance.com/fapi/v1/klines?symbol=%sUSDT&interval=%s&startTime=%s&limit=1000'%(symbol,period,start_time))
        res_list = res.json()
        Klines += res_list
        start_time = res_list[-1][0]
    return pd.DataFrame(Klines,columns=['time','open','high','low','close','amount','end_time','volume','count','buy_amount','buy_volume','null']).astype('float')

df = GetKlines(symbol='DYDX',start='2022-1-1',end='2023-12-7',period='5m')
df = df.drop_duplicates()

Backtesting-Rahmenwerk

Der Backtest nutzt weiterhin das zuvor allgemein verwendete Framework, das mehrere Währungen von unbefristeten USDT-Verträgen unterstützt und einfach und benutzerfreundlich ist.

class Exchange:
    
    def __init__(self, trade_symbols, fee=0.0004, initial_balance=10000):
        self.initial_balance = initial_balance #初始的资产
        self.fee = fee
        self.trade_symbols = trade_symbols
        self.account = {'USDT':{'realised_profit':0, 'unrealised_profit':0, 'total':initial_balance, 'fee':0}}
        for symbol in trade_symbols:
            self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0,'unrealised_profit':0,'fee':0}
            
    def Trade(self, symbol, direction, price, amount):
        
        cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
        open_amount = amount - cover_amount
        self.account['USDT']['realised_profit'] -= price*amount*self.fee #扣除手续费
        self.account['USDT']['fee'] += price*amount*self.fee
        self.account[symbol]['fee'] += price*amount*self.fee

        if cover_amount > 0: #先平仓
            self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount  #利润
            self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
            
            self.account[symbol]['amount'] -= -direction*cover_amount
            self.account[symbol]['hold_price'] = 0 if self.account[symbol]['amount'] == 0 else self.account[symbol]['hold_price']
            
        if open_amount > 0:
            total_cost = self.account[symbol]['hold_price']*direction*self.account[symbol]['amount'] + price*open_amount
            total_amount = direction*self.account[symbol]['amount']+open_amount
            
            self.account[symbol]['hold_price'] = total_cost/total_amount
            self.account[symbol]['amount'] += direction*open_amount
                    
    
    def Buy(self, symbol, price, amount):
        self.Trade(symbol, 1, price, amount)
        
    def Sell(self, symbol, price, amount):
        self.Trade(symbol, -1, price, amount)
        
    def Update(self, close_price): #对资产进行更新
        self.account['USDT']['unrealised_profit'] = 0
        for symbol in self.trade_symbols:
            self.account[symbol]['unrealised_profit'] = (close_price[symbol] - self.account[symbol]['hold_price'])*self.account[symbol]['amount']
            self.account[symbol]['price'] = close_price[symbol]
            self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*close_price[symbol]
            self.account['USDT']['unrealised_profit'] += self.account[symbol]['unrealised_profit']
        self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'],6)

Grid-Backtesting-Funktion

Das Prinzip der Rasterstrategie ist sehr einfach: Verkaufen, wenn der Preis steigt, und kaufen, wenn der Preis fällt. Dabei sind drei spezifische Parameter beteiligt: ​​Anfangspreis, Rasterabstand und Transaktionswert. Die Marktschwankungen von DYDX sind sehr groß. Es ist von anfänglich 8,6 U auf 1 U gefallen und im jüngsten Bullenmarkt wieder auf 3 U gestiegen. Der Standardanfangspreis der Strategie beträgt 8,6 U, was für das Netz sehr ungünstig ist Strategie, aber der Standardparameter-Backtest. Der Gesamtgewinn in zwei Jahren betrug 9200 U, und es gab zeitweise einen Verlust von 7500 U. Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

symbol = 'DYDX'
value = 100
pct = 0.01

def Grid(fee=0.0002, value=100, pct=0.01, init = df.close[0]):
    e = Exchange([symbol], fee=0.0002, initial_balance=10000)
    init_price = init
    res_list = [] #用于储存中间结果
    for row in df.iterrows():
        kline = row[1] #这样会测一根K线只会产生一个买单或一个卖单,不是特别精确
        buy_price = (value / pct - value) / ((value / pct) / init_price + e.account[symbol]['amount']) #买单价格,由于是挂单成交,也是最终的撮合价格
        sell_price = (value / pct + value) / ((value / pct) / init_price + e.account[symbol]['amount'])
        if kline.low < buy_price: #K线最低价低于当前挂单价,买单成交
            e.Buy(symbol,buy_price,value/buy_price)
        if kline.high > sell_price:
            e.Sell(symbol,sell_price,value/sell_price)
        e.Update({symbol:kline.close})
        res_list.append([kline.time, kline.close, e.account[symbol]['amount'], e.account['USDT']['total']-e.initial_balance,e.account['USDT']['fee'] ])
    res = pd.DataFrame(data=res_list, columns=['time','price','amount','profit', 'fee'])
    res.index = pd.to_datetime(res.time,unit='ms')
    return res

Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Der Einfluss des Anfangspreises

Die Einstellung des Anfangspreises wirkt sich auf die Anfangsposition der Strategie aus. Der Standardanfangspreis des Backtests ist gerade der Anfangspreis beim Start, d. h. beim Start wird keine Position gehalten. Wir wissen, dass die Grid-Strategie alle Gewinne abwirft, wenn der Preis auf das ursprüngliche Niveau zurückkehrt. Wenn die Strategie also bei ihrer Einführung den zukünftigen Markt richtig vorhersagen kann, wird sie die Gewinne deutlich steigern. Hier setzen wir den Anfangspreis auf 3U und führen dann einen Backtest durch. Der endgültige maximale Drawdown betrug 9200 U und der endgültige Gewinn 13372 U. Die endgültige Strategie hält keine Positionen. Dieser Gewinn ist der gesamte Volatilitätsgewinn, und die Differenz zwischen dem Gewinn der Standardparameter und diesem Gewinn ist der Positionsverlust, der durch eine ungenaue Beurteilung des Endpreises verursacht wird.

Wenn der Anfangspreis jedoch auf 3U festgelegt ist, wird die Strategie zu Beginn Short gehen und eine große Anzahl von Short-Positionen halten. In diesem Beispiel wird eine Short-Position von 17.000 U direkt gehalten, sodass sie größeren Risiken ausgesetzt ist.

Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Rasterabstandseinstellungen

Der Rasterabstand bestimmt den Abstand zwischen ausstehenden Aufträgen. Je kleiner der Abstand, desto häufiger die Transaktionen, desto geringer der Gewinn einer einzelnen Transaktion und desto höher die Bearbeitungsgebühr. Es ist jedoch zu beachten, dass sich bei kleiner werdendem Rasterabstand und unverändertem Rasterwert die Gesamtposition bei Preisänderungen erhöht und die damit verbundenen Risiken völlig anders sind. Um den Effekt des Rasterabstands rückzutesten, ist es daher erforderlich, den Rasterwert zu konvertieren.

Da der Backtest 5 MK-Zeilendaten verwendet und auf einer K-Zeile nur eine Transaktion durchgeführt wird. Dies entspricht offensichtlich nicht der Realität, insbesondere da die Volatilität digitaler Währungen sehr groß ist. Bei kleineren Abständen werden im Backtesting im Vergleich zum tatsächlichen Handel viele Transaktionen übersehen. Nur größere Abstände haben einen Referenzwert. Die im Rahmen dieses Backtesting-Mechanismus gezogenen Schlussfolgerungen sind nicht korrekt. Durch Backtesting von Orderflow-Daten auf Tick-Level sollte der optimale Rasterabstand 0,005–0,01 betragen.

for p in [0.0005, 0.001 ,0.002 ,0.005, 0.01, 0.02, 0.05]:
    res = Grid( fee=0.0002, value=value*p/0.01, pct=p, init =3)
    print(p, round(min(res['profit']),0), round(res['profit'][-1],0), round(res['fee'][-1],0))
    
0.0005 -8378.0 144.0 237.0
0.001 -9323.0 1031.0 465.0
0.002 -9306.0 3606.0 738.0
0.005 -9267.0 9457.0 781.0
0.01 -9228.0 13375.0 550.0
0.02 -9183.0 15212.0 309.0
0.05 -9037.0 16263.0 131.0

Netztransaktionswert

Wie bereits erwähnt, gilt bei gleicher Volatilität: Je höher der gehaltene Wert, desto höher die risikoproportionale Methode. Solange es sich jedoch nicht um einen schnellen Rückgang handelt, 1 % der Gesamtmittel kombiniert mit 1 % des Rasterabstands sollte mit den meisten Marktbedingungen zurechtkommen. In diesem DYDX-Beispiel löste ein Rückgang von fast 90 % auch einen Margin Call aus. Beachten Sie jedoch, dass DYDX hauptsächlich fällt. Wenn es fällt, geht die Grid-Strategie long und der maximale Rückgang beträgt 100 %. Es gibt jedoch keine Begrenzung für den Anstieg und das Risiko ist viel höher. Daher empfiehlt die Grid-Strategie den Benutzern, Währungen auszuwählen, von denen sie glauben, dass sie Potenzial haben, und nur Long-Positionen einzugehen.

Variabler Regressionspreis

Der Regressionspreis ist der Anfangspreis. Die Differenz zwischen dem aktuellen Preis und dem Anfangspreis sowie die Rastergröße bestimmen, wie viel Position gehalten werden soll. Wenn der Regressionspreis höher als der aktuelle Preis festgelegt ist, wird die Rasterstrategie Long gehen, und umgekehrt. Der Standardregressionspreis ist der Preis, zu dem die Strategie gestartet wurde. Um das Risiko zu reduzieren, empfiehlt es sich, ein Long-Only-Raster zu verwenden. Eine naheliegende Überlegung ist, ob es möglich ist, den Regressionspreis so zu ändern, dass man auch bei steigendem Preis weiterhin Long-Positionen halten kann, ohne sie anpassen zu müssen. selbst. Am Beispiel des BTC-Marktes in diesem Jahr ist er von 15.000 zu Beginn des Jahres auf 43.000 am Ende des Jahres gestiegen. Wenn Sie die Grid-Strategie ab Jahresbeginn ausführen, beträgt der Standardrenditepreis 15.000 und Sie tätigen sowohl Long- als auch Short-Trades. Die spezifischen Renditen sind in der folgenden Abbildung dargestellt. Sie werden auf dem gesamten Weg Geld verlieren, da BTC steigt. Auch wenn der Long-Only-Modus nicht richtig eingerichtet ist, kann der Preis ohne Halten einer Position schnell das Limit überschreiten. Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Erstens wird beim Start der Strategie der Regressionspreis auf das 1,6-fache des Preises zum Zeitpunkt des Starts festgelegt. Auf diese Weise behandelt die Grid-Strategie den Preis als vom 1,6-fachen auf den aktuellen Preis fallend und beginnt, die Long-Position zu halten. Position, die durch diesen Teil der Preisdifferenz entsteht. Wenn der nachfolgende Preis /1,6 überschreitet, wird der ursprüngliche Preis zurückgesetzt, sodass immer mindestens 60 % der Differenz für Long-Positionen zur Verfügung stehen. Die Backtest-Ergebnisse lauten wie folgt: Detaillierte Erläuterung der Parameteroptimierung der unbefristeten Netzstrategie

Wenn Sie optimistischer in Bezug auf den Markt sind, können Sie dieses Verhältnis natürlich auf einen größeren Wert einstellen, und der endgültige Gewinn wird entsprechend steigen. Wenn der Markt fällt, erhöht diese Einstellung natürlich auch das Risiko, Positionen zu halten.