Optimisation des paramètres stratégiques de la grille de contrats permanents

Auteur:Le foin, Créé à partir de: 2023-12-08

img

La stratégie de la grille permanente est une stratégie classique très populaire sur les plateformes. Elle n'utilise pas de jetons, peut être utilisée comme un levier et est beaucoup plus pratique que la grille instantanée. Mais comme il n'est pas possible de retester directement sur la plate-forme de quantification des inventeurs, ce qui est préjudiciable à la sélection des pièces et à l'optimisation des paramètres, cet article présentera le processus de retest complet de Python, qui comprend tous les aspects de la collecte de données, du retest du cadre, des fonctions de mesure et de l'optimisation des paramètres, que vous pouvez essayer vous-même dans le notebook juypter.

Collecte de données

En règle générale, les données de ligne K suffisent, pour plus de précision, les cycles de ligne K plus petits sont meilleurs, mais pour équilibrer le temps de retouche et la quantité de données, cet article utilise 5 min pour retoucher les données des deux dernières années, la quantité de données finale dépassant les lignes 20W, la monnaie choisit DYDX. Bien sûr, les devises spécifiques et les cycles de ligne K peuvent être choisis en fonction de vos intérêts.

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()

Cadre de réévaluation

La réévaluation continue de choisir le cadre précédemment couramment utilisé pour soutenir l'USDT en tant que monnaie multi-monnaie contractuelle permanente, simple et pratique.

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)

Fonction de retouche de grille

Le principe de la stratégie de la grille est simple: les ventes et les achats sont en hausse et en baisse, ce qui implique trois paramètres: le prix initial, l'intervalle de grille et la valeur de la transaction.img

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

img

L'impact du prix initial

Le prix de l'initiation affecte la position initiale de la stratégie, le prix initial par défaut que nous venons de répéter est le prix initial au démarrage, c'est-à-dire ne pas avoir de position au démarrage. Et nous savons que la stratégie de la grille rapporte tous les bénéfices au moment où le prix revient au début, de sorte que si la stratégie est en mesure de prévoir correctement les événements futurs au démarrage, les bénéfices seront considérablement améliorés.

Cependant, le prix initial est de 3 U, la stratégie consiste à détenir une quantité importante d'entrepôts vides au début, dans cet exemple, en détenant directement des billets vides de 17 000 U, ce qui représente un risque plus important pour les utilisateurs.

img

Sélectionnez l'écart de grille

L'intervalle de grille détermine la distance entre les ordres, et il est évident que plus l'intervalle est petit, plus les transactions sont fréquentes, moins le profit d'un seul billet est élevé, et plus les frais de traitement sont élevés. Mais il est à noter que l'intervalle de grille devient plus petit et que la valeur du grille ne change pas, lorsque les prix changent, le total des avoirs augmente, les risques sont complètement différents.

Étant donné que le retouche utilise des données de ligne de 5 mK et ne traite qu'une seule fois sur une ligne de K. Cela est manifestement irréaliste, en particulier si la volatilité de la monnaie numérique est très élevée et que de plus petits intervalles manquent beaucoup de transactions dans le retouche par rapport au disque réel, il suffit d'augmenter l'intervalle pour avoir une valeur de référence. Dans ce mécanisme de retouche, les conclusions ne sont pas exactes.

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

La valeur des transactions sur le réseau

Comme nous l'avons mentionné précédemment, lorsque la volatilité est simultanée, la valeur de la détention est plus élevée, le risque et d'autres méthodes proportionnelles, mais à condition qu'il n'y ait pas de chute rapide, 1% du capital total et 1% de l'écart de grille devraient être en mesure de faire face à la plupart des marchés. Dans l'exemple de DYDX, une chute de près de 90% a également déclenché une explosion.

Prix de retour variable

Le prix de rebond est le prix initial, la différence entre le prix actuel et le prix initial et la taille du filet déterminent le nombre de positions à détenir. Si le prix de rebond est placé au-dessus du prix actuel, la stratégie du filet sera plus élevée et sera à son tour vide. Le prix de rebond par défaut est le prix au début de la stratégie.img

Lorsque la stratégie est lancée, le prix de retour est fixé à 1,6 fois le prix de lancement, de sorte que la stratégie de filet commence à détenir la partie de l'écart générée par la prise de position lorsque le prix est tombé de 1,6 fois au prix actuel. Si le prix ultérieur dépasse le prix de retour / 1.6, le prix initial est réinitialisé, ce qui maintient toujours au moins 60% de l'écart utilisé pour faire de l'écart.img

Bien sûr, si vous êtes plus optimiste sur le marché, vous pouvez augmenter ce ratio, et les bénéfices finaux augmenteront en conséquence, bien sûr, si le marché baisse, cela augmente également le risque de détention.


Plus de

Équipement électrique et électroniquePourquoi fmz ne peut-il pas retranscrire directement la stratégie de la grille?