Quand j'ai écritRecherche sur la stratégie de couverture multi-monnaie des contrats à terme de Binance, j'ai également publié un moteur de backtest. Et le premier rapport était basé sur le backtest d'une heure de K-line, qui a vérifié l'efficacité de la stratégie. Mais le temps de sommeil de la stratégie open source réelle est de
Tout d'abord, qu'est-ce que la ligne K historique? Une ligne K contient quatre prix de haut, d'ouverture, de bas, de fermeture, les deux premiers
La première est la question du temps. Le temps du prix le plus élevé et le prix le plus bas des données de la ligne K ne sont pas donnés et ne doivent pas être considérés, mais les prix d'ouverture et de fermeture les plus importants ne sont pas les temps d'ouverture et de fermeture. Même les variétés de trading les moins populaires n'ont souvent pas de transaction pendant plus de dix secondes, et lorsque nous testons la stratégie multi-variété, nous supposons souvent que leur prix d'ouverture et leur prix de fermeture sont les mêmes, ce qui est également basé sur le backtest du prix de fermeture.
Imaginez utiliser la ligne de niveau minute K pour vérifier l'arbitrage de deux variétés. La différence entre elles est généralement de 10 yuans ((ou dollars). Maintenant, à 10:01, le prix de clôture du contrat A est de 100, le contrat B est de 112 et la différence est de 12 yuans. Ainsi, la stratégie commence à se protéger. À un certain moment, la différence de prix est revenue et la stratégie a réalisé un profit de retour de 2 yuans.
mais la situation réelle peut être que à 10h45, le contrat A a produit une transaction de 100 yuans, après quoi il n'y a pas eu de transaction, le contrat B a eu une transaction de 112 yuans à 10h58, à 10h01:00 Les deux prix n'existent pas. Quel est le prix du marché à ce moment-là, et combien l'opération de couverture peut-elle obtenir? Je ne peux pas savoir. Une situation possible est: à 10h58, le prix de l'ordre en attente de 101.9
à102.1
Il n'y a pas de différence de 2 yuans.
Le deuxième est le problème du matchmaking. Le vrai matchmaking est la priorité du prix et la priorité du temps. Si l'acheteur dépasse le prix de
La dernière est l'impact de la stratégie elle-même sur le marché. Si c'est un backtest de petits fonds, l'impact n'est pas grand. Mais si le volume de transactions est important, il aura un impact sur le marché. Non seulement le glissement de prix sera important lorsque vous passez un ordre de grand volume, si vous achetez un ordre long exécuté, ce type d'action saisira réellement les ordres d'autres traders qui voulaient à l'origine acheter, l'effet "papillon" aura un impact sur le marché. Cet effet ne peut pas être quantifié. Nous ne pouvons que dire par expérience que le trading à haute fréquence ne peut accueillir que de petits fonds.
FMZ fournit un backtest au niveau réel, qui peut obtenir des données historiques réelles20 layer depth price
, deuxième niveau en temps réelTicks
, Each Individual Transaction
Sur la base de ces caractéristiques, FMZ a créé une fonction de lecture des transactions en temps réel.
Cette quantité de données de backtest est très importante, et la vitesse de backtest est également très lente, généralement seulement deux jours. Pour les stratégies à fréquence relativement élevée ou à temps critique, un backtest au niveau du marché réel est nécessaire.
Le mécanisme de matchmaking actuel est que si l'ordre d'achat est supérieur à la
Il y a trop peu d'informations dans la ligne K, et la profondeur de prix peut aussi être une fausse profondeur, mais il y a une sorte de données qui est la volonté de transaction réelle du marché, qui reflète l'historique de transaction le plus réel, c'est,Each Individual Transaction
Cet article proposera un système de backtest basé sur le flux des commandes, qui réduira considérablement le volume des données de backtest au niveau du marché réel et simulera dans une certaine mesure l'impact du volume des transactions sur le marché.
J'ai téléchargé la transaction des 5 derniers jours Binance XTZ contrat perpétuel (adresse de téléchargement:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv), en tant que variété peu populaire, elle comporte un total de 213000 données de transactions, regardons d'abord la composition des données:
[['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'],
Les données sont une liste bidimensionnelle, triée par ordre chronologique. Les significations spécifiques sont les suivantes: nom de la variété, prix de la transaction, horodatage de la transaction, quantité de la transaction, s'il s'agit d'une transaction active d'ordre de vente.Maker
et le vendeur est un actifTaker
, les dernières données sontTrue
.
Tout d'abord, selon la direction de la transaction, vous pouvez spéculer assez précisément sur le
Selon le flux des commandes, il peut être assorti de la façon suivante: prenez un ordre d'achat comme exemple, le prix estprice
, la quantité de commande estamount
, puis acheter et vendre 1 à ce moment sontbid
etask
Si le taux deprice
est inférieure àask
et supérieur àbid
, alors il est jugé commemaker
d'abord, et la priorité peut être assortie pour faire une transaction, alors toutes les transactions avec un prix de transaction inférieur ou égal àprice
pendant le temps d'existence de l'ordre sera correspondre à cette commande (siprice
est inférieure ou égale àbid
Les ordres dont le prix de transaction est inférieur àprice
sont correspondantes à cet ordre.)
Le prix correspondant estprice
, et le volume est le volume de transactions deEach Individual Transaction
, jusqu'à ce que l'ordre soit entièrement complété ou l'ordre annulé.ask
, il est jugé comme untaker
Après cela, pendant la durée de l'ordre, toutes les transactions dont le prix de transaction est inférieur ou égal àprice
sont assortis à cet ordre, et le prix d'appariement est le prix de transaction de l'ordreEach Individual Transaction
La distinction entremaker
ettaker
Les stratégies à haute fréquence sont essentiellement parce que l'échange encourage les commandes en attente et il y a des réductions pour les frais de transaction.
Il est facile de voir un problème avec ce type de correspondance.taker
, la situation réelle est qu'il peut être exécuté immédiatement, plutôt que d'attendre une nouvelle commande pour être assorti avec elle. tout d'abord, nous n'avons pas considéré le volume des commandes en attente, même s'il y a des données, directement juger la transaction a également changé la profondeur de prix, affectant le marché.
En fonction de la correspondance des nouveaux ordres, il équivaut à remplacer les ordres existants dans l'histoire par vos ordres. En tout cas, il ne dépassera pas la limite du volume de négociation du marché lui-même, et le profit final ne peut pas dépasser le profit maximal généré par le marché. Une partie du mécanisme de correspondance affecte également le volume des ordres, ce qui à son tour affecte les revenus de la stratégie, reflétant quantitativement la capacité de la stratégie. Il n'y aura pas de backtest traditionnel, lorsque le montant des fonds double et le gain double.
Il y a encore quelques petits détails. Si le prix d'achat de l'ordre est égal à
Les objets d'échange peuvent se référer à l'introduction au début, essentiellement inchangée, ajoutant seulement la différence entremaker
ettaker
Les résultats de l'analyse de l'efficacité des tests de compatibilité sont les suivants:
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
Quelques détails à noter:
Lorsqu'il y a une nouvelle transaction, nous devons d'abord faire correspondre l'ordre, puis placer l'ordre selon le dernier prix.
Chaque ordre a deux attributs: makermaker
, et lorsque le prix d'achat est supérieur à Priority matching
, priority
détermine si le prix est égal ou non au prix d'achat, et le fabricant détermine les frais de transaction.
Lemaker
etpriority
Si un achat important a été effectué et dépasse la capacité du marché, le volume restant sera le volume de vente au détail.maker
.
Stratégieinterval
Il s'agit d'un problème de qualité.
Enfin, c'est la phase de backtest réelle. Voyons backtest une des stratégies de grille les plus classiques ici pour voir si nous pouvons obtenir les résultats attendus. Le principe de la stratégie est que chaque fois que le prix augmente de 1%, nous tenons un ordre court d'une certaine valeur (inverse, nous tenons un ordre long), calculer l'ordre d'achat et de vente à l'avance. je ne vais pas vous montrer le code source. ils sont tous encapsulés dans leGrid('XTZ', 100, 0.3, 1000, maker_fee=-0.00002, taker_fee=0.0003)
fonction, les paramètres sont les suivants: paire de négociation, déviation du prix de la valeur de détention de 1%, densité des ordres en attente est de 0,3%, intervalle de sommeilms
, les frais de commande en attente et les frais de commande exécutés.
Le prix du marché de XTZ
Nous effectuons d'abord un backtest sur l'effet des différentes positions de détention sur le rendement des bénéfices.
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'])
Un total de quatre groupes ont été testés en arrière-plan, la valeur des positions de détention était de 100, 1000, 10000, 100 000, et le temps total de test en arrière-plan était de 1,3 s. Les résultats sont les suivants:
{'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}
On peut voir que les bénéfices finaux réalisés sont respectivement de 28,4%, 27,5%, 26,9% et 22,6% de la valeur de la position de détention. Cela est également conforme à la situation réelle. Plus la valeur de la position de détention est élevée, plus la valeur de l'ordre en attente est élevée, plus il est probable qu'une transaction partielle se produise et plus le gain final réalisé est petit par rapport au montant de l'ordre en attente. Le tableau suivant est une comparaison des rendements relatifs de la valeur de la position de 100 et 10000 respectivement:
Nous pouvons également effectuer un backtest sur l'impact de différents paramètres sur les revenus de backtest, tels que la densité des commandes en attente, le temps de sommeil, les frais de transaction, etc. Prenons le temps de sommeil à titre d'exemple, changez-le à 100 ms, et comparez le temps de sommeil à 1000 ms pour voir le retour de profit.
{'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}
La stratégie de grille pour la mise en place de plusieurs ensembles d'ordres montre également l'importance de la stratégie de grille pour le placement de plusieurs ensembles d'ordres.
Cet article propose de manière innovante un nouveau système de backtest basé sur le flux d'ordres, qui peut partiellement simuler la situation de correspondance des ordres en attente, des ordres exécutés, des ordres partiellement exécutés, des retards, etc., et reflète partiellement l'impact du montant des fonds de stratégie sur les revenus. Pour les stratégies à haute fréquence et de couverture, il a une valeur de référence importante. Le backtest de haute précision indique la direction de l'optimisation des paramètres de stratégie. Il a également été vérifié pendant longtemps.