3
Suivre
1444
Abonnés

Un système de backtesting à haute fréquence basé sur la transaction par transaction et les défauts du backtesting K-line

Créé le: 2020-06-04 16:48:02, Mis à jour le: 2024-12-10 20:32:01
comments   7
hits   5112

Un système de backtesting à haute fréquence basé sur la transaction par transaction et les défauts du backtesting K-line

Je suis làStratégie de couverture multidevises Binance Short Over-Rising Long Over-SlumpingUn moteur de backtesting a également été publié en même temps. Le premier rapport a vérifié l’efficacité de la stratégie sur la base d’un backtest K-line d’une heure. Cependant, le temps de dormance réel de la stratégie publique est de 1 s, ce qui correspond à une stratégie à très haute fréquence. Il est évidemment impossible d’obtenir des résultats précis en effectuant des tests rétrospectifs avec une ligne K horaire. Ajouté plus tardBacktest de la ligne des minutesEn conséquence, le bénéfice du backtesting a beaucoup augmenté, mais il est toujours impossible de déterminer quels paramètres doivent être utilisés dans la situation de deuxième niveau, et la compréhension de l’ensemble de la stratégie n’est pas très claire. La raison principale est l’inconvénient important du backtesting basé sur la ligne K.

Problèmes avec le backtesting K-line

Tout d’abord, qu’est-ce que la ligne K historique ? Les données d’une ligne K comprennent quatre prix : ouverture haute et clôture basse, deux heures de départ et un volume de négociation de plage. La plupart des plateformes et cadres quantitatifs sont basés sur le backtesting K-line, et la plateforme quantitative FMZ fournit également un backtesting au niveau des ticks. Le backtesting K-line est très rapide et ne pose aucun problème dans la plupart des cas, mais il présente également de très graves défauts, en particulier lors du backtesting de stratégies multi-variétés et de stratégies à haute fréquence, il est presque impossible de tirer des conclusions correctes.

Le premier problème est celui du temps. L’heure des prix les plus élevés et les plus bas des données de la ligne K n’est pas donnée, il n’est donc pas nécessaire de la prendre en compte. Mais les prix d’ouverture et de clôture les plus importants ne commencent pas à partir de l’ouverture et de la fermeture. heure de fermeture. Même pour les produits de trading les moins populaires, il n’y a souvent pas de trading pendant plus de dix secondes. Lorsque nous testons des stratégies multi-produits, nous partons souvent du principe que leurs prix d’ouverture et de clôture sont simultanés. C’est également la base du backtesting du prix de clôture.

Imaginez que vous utilisiez la ligne des minutes pour tester rétrospectivement l’arbitrage de deux variétés. Leur différence de prix est généralement de 10 yuans. On constate maintenant qu’à 10h01, le prix de clôture du contrat A est de 100 et celui du contrat B de 112, avec un prix différence de 12 yuans. Par conséquent, la stratégie commence à se couvrir. À ce moment-là, la différence de prix est revenue et la stratégie a généré un bénéfice de retour de 2 yuans.

La situation réelle peut être qu’à 10h00:45, le contrat A a généré une transaction de 100 yuans, et qu’il n’y a eu aucune transaction par la suite. À 10h00:58, le contrat B a généré une transaction de 112 yuans. À 10h01, les deux prix n’existent pas, quel est le prix du marché à ce moment-là et quelle différence de prix peut être obtenue en se couvrant ? Il n’y a aucun moyen de le savoir. Un scénario possible est le suivant : à 10h00:58, le prix acheteur-vendeur du contrat A est de 101,9-102,1, et il n’y a aucune différence de prix de 2 yuans. Cela pourrait grandement induire en erreur notre optimisation stratégique.

Le deuxième problème est celui de la correspondance. Une véritable correspondance donne la priorité au prix et au temps. Si l’acheteur dépasse le prix demandé, la transaction sera généralement conclue directement au prix demandé. Dans le cas contraire, elle entrera dans le carnet d’ordres et attendra. Les données K-line n’ont évidemment pas de prix d’achat et de vente, et il est impossible de simuler la correspondance au niveau détaillé.

Enfin, il y a l’impact de la stratégie elle-même sur le marché. S’il s’agit d’un backtest à petit capital, l’impact ne sera pas significatif. Mais si le volume des échanges représente une part importante, cela aura un impact sur le marché. Non seulement le glissement de prix sera important lorsque la transaction est exécutée immédiatement, mais si votre ordre d’achat est exécuté via un backtesting, il préemptera en fait les transactions d’autres traders qui voulaient initialement acheter, et l’effet papillon aura un impact sur le prix. marché. Cet impact ne peut pas être quantifié, et nous pouvons seulement dire, sur la base de notre expérience, que le trading haute fréquence ne peut accueillir que de petits fonds.

Backtesting basé sur la profondeur et les ticks en temps réel

FMZ fournit des tests rétrospectifs en temps réel, qui peuvent obtenir une profondeur historique réelle de 20 niveaux, un tick de deuxième niveau en temps réel, des données transaction par transaction, et sur cette base,Fonction de lecture en temps réel. La quantité de données de backtesting est extrêmement importante et la vitesse est très lente, généralement seulement deux jours. Pour les stratégies à fréquence relativement élevée ou les stratégies qui nécessitent un jugement temporel strict, un backtesting en temps réel est nécessaire. Les paires de transactions et les périodes de temps collectées par FMZ ne sont pas longues, mais il existe plus de 70 milliards de données historiques. Le mécanisme de correspondance actuel est le suivant : si l’ordre d’achat est supérieur à l’ordre de vente, il sera entièrement mis en correspondance immédiatement, quel que soit le volume ; s’il est inférieur à l’ordre de vente, il entrera dans la file d’attente de correspondance. Un tel mécanisme de backtesting résout les deux premiers problèmes du backtesting K-line, mais il ne peut toujours pas résoudre le dernier problème. Et comme la quantité de données est si importante, la vitesse et la plage de temps des tests rétrospectifs sont limitées.

Un système de backtesting à haute fréquence basé sur la transaction par transaction et les défauts du backtesting K-line

Mécanisme de backtesting basé sur le flux d’ordres de transaction

Il y a trop peu d’informations sur la ligne K et la profondeur peut être fausse, mais il existe un type de données qui reflète la véritable intention de transaction du marché et reflète l’historique de transaction le plus authentique - c’est-à-dire transaction par transaction. Cet article proposera un système de backtesting à haute fréquence basé sur le flux d’ordres, qui réduira considérablement la quantité de données pour le backtesting en temps réel et simulera dans une certaine mesure l’impact du volume des transactions sur le marché.

J’ai téléchargé les enregistrements de transactions du contrat perpétuel Binance XTZ au cours des 5 derniers jours (adresse de téléchargement : https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv). En tant que produit peu populaire, il y a 213 000 transactions dans total. Données, 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 heure de transaction. Les significations spécifiques sont : le nom du produit, le prix de la transaction, l’horodatage de la transaction, la quantité de la transaction et s’il s’agit d’une transaction d’ordre de vente active. Il y a des acheteurs et des vendeurs, et chaque transaction comprend un acheteur et un vendeur. Si l’acheteur est un teneur de marché et le vendeur est un preneur, la dernière donnée est vraie.

Premièrement, en fonction de la direction de la transaction, les prix d’achat et de vente sur le marché peuvent être déduits assez précisément. S’il s’agit d’un ordre de vente actif, le prix d’achat à ce moment est le prix de la transaction. S’il s’agit d’un ordre d’achat actif, le prix de la transaction est le prix de la transaction. Le prix de vente est le prix de transaction. S’il y a une nouvelle transaction, la nouvelle cotation sera mise à jour. S’il n’est pas mis à jour, le résultat précédent sera conservé. Il est facile de déduire qu’au dernier moment des données ci-dessus, le prix d’achat était de 2,903 et le prix de vente était de 2,904.

Selon le flux d’ordres, la correspondance peut être effectuée comme suit : en prenant un ordre d’achat comme exemple, le prix est le prix, la quantité commandée est le montant et les ordres d’achat et de vente sont respectivement l’offre et la demande. Si le prix est inférieur à la demande et supérieur à l’offre, il est d’abord déterminé comme étant un maker et peut être associé à l’ordre en premier. Ensuite, pendant la durée d’existence de l’ordre, toutes les transactions avec un prix de transaction inférieur ou égal à Un prix égal au prix sera associé à cet ordre (si le prix est inférieur au prix demandé, l’ordre sera associé au prix offert). Si le prix offert est égal ou supérieur au prix offert, l’ordre ne peut pas être négocié en premier . Tous les ordres dont le prix de transaction est inférieur au prix de l’offre seront mis en correspondance avec cet ordre. Le prix de mise en correspondance est le prix de l’offre et le volume de transaction est le volume de transaction de chaque transaction jusqu’à ce que l’ordre soit entièrement exécuté ou annulé. Si le prix est supérieur au prix demandé, il est considéré comme preneur. Après cela, toutes les transactions avec un prix de transaction inférieur ou égal au prix au cours de la durée d’existence de l’ordre seront mises en correspondance avec cet ordre, et le prix correspondant sera être le prix de transaction de la transaction. La distinction entre les créateurs et les preneurs est due au fait que les bourses encouragent essentiellement le placement d’ordres et offrent des frais de transaction préférentiels. Pour les stratégies à haute fréquence, cette distinction doit être prise en compte.

Il est facile de voir un problème avec ce type de correspondance. Si l’ordre est un preneur, la situation réelle est qu’il peut être exécuté immédiatement, plutôt que d’attendre que de nouveaux ordres lui correspondent. Tout d’abord, nous n’avons pas pris en compte le volume des commandes en attente. Même s’il y avait des données, juger directement la transaction modifierait la profondeur et affecterait le marché. La mise en correspondance basée sur de nouveaux ordres équivaut à remplacer les ordres réels de l’historique par vos ordres. Dans tous les cas, il ne dépassera pas la limite du volume de transactions propre au marché et le bénéfice final ne dépassera pas le bénéfice maximum généré par le marché. Certains mécanismes de correspondance affectent également le volume de transactions des ordres, ce qui affecte à son tour les rendements de la stratégie et reflète quantitativement la capacité de la stratégie. Il n’y aura pas de backtest traditionnel où le bénéfice sera doublé si le montant des fonds est doublé.

Il y a quelques petits détails. Si le prix d’achat de l’ordre est égal au prix d’achat, il existe en fait toujours une certaine probabilité qu’il soit égalé au prix d’achat. Il est nécessaire de prendre en compte la priorité de l’ordre en attente et la probabilité de transaction, etc. C’est relativement compliqué et ne sera pas abordé ici.

Code correspondant

Les objets d’échange peuvent se référer à l’introduction du début, qui reste fondamentalement inchangée. Seule la différence entre les frais de création et de prise en charge est ajoutée et la vitesse de backtesting est optimisée. Ce qui suit présentera principalement le code correspondant.

    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

Quelques détails à noter :

  • 1. Lorsqu’une nouvelle transaction est effectuée, vous devez d’abord faire correspondre la commande, puis passer une commande en fonction du dernier prix.
  • 2. Chaque ordre possède deux attributs : maker - s’il s’agit d’un maker, priority - priorité correspondante. Prenons l’exemple d’un ordre d’achat : lorsque le prix d’achat est inférieur au prix demandé, il est marqué comme maker, et lorsque le prix demandé est inférieur au prix demandé, il est marqué comme maker. le prix d’achat est supérieur au prix d’achat, il est marqué comme un ordre d’achat. Correspondance prioritaire, la priorité détermine si le prix est égal au prix de l’offre et si le fabricant détermine les frais de traitement.
  • 3. Le créateur et la priorité de l’ordre sont mis à jour. Par exemple, si un ordre d’achat important est placé et qu’il dépasse le prix du marché, lorsqu’un prix supérieur au prix d’offre apparaît, le volume de négociation restant sera celui du créateur.
  • 4. L’intervalle de la stratégie est nécessaire, ce qui peut représenter le retard du marché.

Stratégies de grille de backtesting

Nous arrivons enfin à l’étape du backtesting proprement dit. Nous allons backtester l’une des stratégies de grille les plus classiques pour voir si elle atteint les résultats escomptés. Le principe de la stratégie est que chaque fois que le prix augmente de 1%, nous détenons un ordre court d’une certaine valeur (ou vice versa, détenons un ordre long), calculons les ordres d’achat et de vente et les plaçons à l’avance. Le code n’est pas publié. Encapsuler tout le code dansGrid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)Dans la fonction, les paramètres sont : paire de trading, écart de prix de 1 % de la valeur de détention, densité d’ordre de 0,3 %, intervalle de veille en ms, frais de création d’ordre et frais de prise d’ordre.

Au cours des cinq derniers jours, le marché XTZ a traversé une phase volatile, ce qui est très adapté au réseau. Un système de backtesting à haute fréquence basé sur la transaction par transaction et les défauts du backtesting K-line

Nous testons d’abord l’impact de différentes tailles de position sur les rendements. Les rendements mesurés par le mécanisme de backtesting traditionnel augmenteront certainement proportionnellement à l’augmentation des positions.

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'])

Au total, quatre groupes ont été rétrotestés, avec des valeurs de maintien de 100, 1000, 10000 et 100000 respectivement, et le temps total de rétrotest é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 constater que les bénéfices finaux réalisés étaient respectivement de 28,4 %, 27,5 %, 26,9 % et 22,6 % de la valeur de la position. Cela correspond également à la situation actuelle. Plus la valeur de la position 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 le bénéfice final réalisé sera plus faible par rapport à la valeur de l’ordre en attente. volume de commande en attente. Le graphique suivant compare les rendements relatifs des avoirs ayant des valeurs respectives de 100 et 10 000 : Un système de backtesting à haute fréquence basé sur la transaction par transaction et les défauts du backtesting K-line

Nous pouvons également tester l’impact de différents paramètres sur les retours de backtesting, tels que la densité des commandes, le temps de dormance, les frais de traitement, etc. Prenons l’exemple du temps de sommeil, changez-le à 100 ms et comparez-le avec le temps de sommeil de 1 000 ms pour voir les avantages. Les résultats du backtest sont les suivants :

{'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}

Le bénéfice a légèrement augmenté. Cela est dû au fait que la stratégie ne place qu’un seul ensemble d’ordres. Certains ordres ne peuvent pas bénéficier des fluctuations de prix car ils ne peuvent pas être modifiés à temps. La réduction du temps d’inactivité a amélioré ce problème. Cela illustre également l’importance de placer plusieurs groupes de commandes dans une stratégie de grille.

Résumer

Cet article propose de manière innovante un nouveau système de backtesting basé sur le flux d’ordres, qui peut simuler partiellement les conditions de correspondance des ordres en attente, des ordres pris, des transactions partielles, des retards, etc., et refléter partiellement l’impact des fonds de stratégie sur les rendements. Il contient des références importantes valeur pour les stratégies de couverture, et les tests rétrospectifs de haute précision indiquent la direction à suivre pour optimiser les paramètres de la stratégie. Cela a également été vérifié par des échanges réels à long terme. Il contrôle également mieux la quantité de données requises pour le backtesting, et la vitesse du backtesting est également très rapide.