[Binance Championship] Stratégie 3 du contrat de livraison de Binance - Couverture par papillon

Auteur:Je ne sais pas., Créé à partir de: 2022-11-11 18:17:46, mis à jour à partir de: 2023-09-14 20:32:10

img

Récemment, les contrats à terme Binance ont lancé le deuxième Binance Championship (adresse:https://www.binancezh.com/cn/futures/activity/anniversary-competition/129-38599440La plateforme officielle FMZ Quant a également organisé une équipe, que l'on peut trouver en recherchant FMZ Quant directement. Actuellement, il y a un peu plus de 100 personnes. Bienvenue à nous rejoindre. Après cela, vous pouvez ajouter le WeChat du chef d'équipe: fmz_zhangchao, répondre Binance, et nous vous inviterons à rejoindre le groupe WeChat.

La stratégie préparée pour le championnat Binance est la couverture papillon du contrat de livraison. Cet article est le rapport de recherche de la stratégie. Attention: les stratégies sont uniquement à titre de référence. Vous pouvez présenter vos propres idées d'optimisation sur cette base. Vous êtes également invités à partager. Le rapport peut être utilisé dans l'environnement de recherche du site Web FMZ directement (cliquez dans le coin supérieur droit pour télécharger et télécharger dans l'analyse).

img

1. Motifs stratégiques

La couverture doit trouver une différence de prix stable. Lorsque la différence de prix est trop grande, couvrir la différence de prix. Lorsque la différence de prix est trop petite, couvrir la différence de prix. Lorsque la différence de prix revient pour fermer la position, vous gagnerez la différence de prix. Si les contrats à terme et les spots sont couverts, lorsque le prix des contrats à terme non livrés est beaucoup plus élevé que le prix au comptant, vous pouvez couper le contrat à terme et couvrir le prix au comptant pour couper la différence de prix. Il existe également des couvertures intertemporelles de contrats avec des délais de livraison différents, avec des contrats à terme et des spots couverts, ils peuvent également couvrir les différences de prix longues. Les contrats à terme et les spots et les contrats à terme croisés sont des stratégies courantes avec une concurrence féroce.

2. Les principes de la stratégie

Les contrats de monnaie standard de Binance, tels que BTC et ETH, ont trois contrats en même temps, à savoir, perpétuel BTCUSD_ PERP, BTCUSD_200925 du trimestre en cours, BTCUSD_ 201225 du trimestre suivant. Les contrats perpétuels peuvent être utilisés comme spots. Généralement, il existe trois différentiels de prix pour la couverture de deux contrats: trimestre actuel perpétuel, trimestre prochain perpétuel et trimestre prochain courant. L'arbitrage papillon nécessite trois contrats. La différence est (quartal suivant - trimestre actuel) - (quartal actuel - perpétuel), c'est-à-dire la différence = trimestre suivant + perpétuel - 2 * trimestre actuel.

3. Sécurisation de l'espace

J'ai exploré les données de 5min K-line de Binance du 14 août au 14 septembre, qui peuvent être lues directement (en raison du décalage horaire, le décalage horaire affiché est de 8h).

Dans [4]:

# Libraries to be imported
import pandas as pd
import requests
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import time
%matplotlib inline

Dans [12]:

#Read the data, you can also upload the data to the FMZ forum, which can be referenced in the "Analyze" directly
df = pd.read_csv('https://www.fmz.com/upload/asset/1420b2081ecd122522d.csv',index_col = 0)
df.index = pd.to_datetime(df.index)
df.tail(3)

À l'extérieur [12]:

img

Tout d'abord, jetons un coup d'œil à la différence de prix entre les contrats Bitcoin. Le 17 août, le prix de Bitcoin a augmenté rapidement de 500u. D'une manière générale, le contrat livré était à une prime par rapport au prix au comptant, et le prix au comptant a augmenté. L'attente pour l'avenir sera plus optimiste. La différence de prix entre le contrat non livré et la perpétuité deviendra plus grande. Par exemple, la différence de prix entre le trimestre suivant et la perpétuité sera de 700u. Avec la baisse du prix de Bitcoin en septembre, les attentes des gens vont se détériorer rapidement, la différence de prix entre le trimestre suivant et la perpétuité est tombée à environ 150u, et il n'y avait presque pas de différence de prix entre le trimestre actuel et la perpétuité. Si la couverture entre le trimestre suivant et la perpétuité était effectuée, seul le retour du prix à long terme pourrait être effectué. Si la différence de prix entre 400-600 était décidée à être effectu

Dans [18]:

#Perpetual price
df['BTCUSD_PERP'].dropna().plot(figsize=(15,6),grid=True);

À l'extérieur [1]:

img

Dans [15]:

# Price difference of next quarter - perpetual
(df['BTCUSD_201225']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);

Extrait [1]:

img

Dans [16]:

# Price difference of current quarter - perpetual
(df['BTCUSD_200925']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);

Extrait [1]:

img

Dans [17]:

# Price difference of next quarter - current quarter
(df['BTCUSD_201225']-df['BTCUSD_200925']).dropna().plot(figsize=(15,6),grid=True);

Extrait [1]:

img

Alors, comment la différence de prix change-t-elle à ce moment-là? Comme on peut le voir sur la figure ci-dessous, la différence de prix récente est stable à 100-200u depuis longtemps. Même la forte baisse au début de septembre n'a pas beaucoup affecté, nous laissant beaucoup de place pour des arbitrages répétés.

Lorsque le prix au comptant fluctue, les deux contrats non expirés reflètent simultanément l'attente de l'avenir. Le processus de réduction de la différence de prix peut compenser cette fluctuation dans une large mesure et la performance est relativement stable.

Dans [19]:

#(next quarter - current quarter)-(current quarter - perpetual)
(df['BTCUSD_201225']-df['BTCUSD_200925']-(df['BTCUSD_200925']-df['BTCUSD_PERP'])).dropna().plot(figsize=(15,6),grid=True);

Extrait [1]:

img

Dans [22]:

#The price difference of ETH
(df['ETHUSD_201225']+df['ETHUSD_PERP']-2*df['ETHUSD_200925']).dropna().plot(figsize=(15,6),grid=True);

Extrait[22]:

img

4. Test de retour de la stratégie

Pour gagner du temps (juste paresse), le backtest utilise toujours le moteur standard USDT de la dernière stratégie Binance Championship. Bien qu'il puisse y avoir des erreurs, il peut également expliquer le problème. Le moteur de backtesting est placé à la fin de ce rapport. Lorsque vous exécutez le code, vous devriez voir la fin de l'article. La stratégie standard de devise peut envisager la couverture si vous voulez gagner USDT, et ce n'est pas compliqué.

La ligne médiane de la différence de prix est suivie par l'EMA, et la position est contrôlée par la grille, c'est-à-dire que chaque fois que la différence est ouverte (comme 30), N actions vont courtes, et vice versa.

Voici les codes et les résultats spécifiques du backtesting BTC et ETH. La performance est conforme aux attentes. Étant donné que l'ETH et LINK ont une plus grande volatilité et que la différence de prix est plus stable, la performance est meilleure. Notez que les frais de service ici sont de 0,02%, et les frais de service par défaut pour les preneurs de vip0 sur Binance sont de 0,04%.

Dans [39]:

trade_symbols = ['BTCUSD_201225', 'BTCUSD_200925', 'BTCUSD_PERP']
account = []
diff = df['BTCUSD_201225']+df['BTCUSD_PERP']-2*df['BTCUSD_200925']
diff_mean = diff.ewm(alpha=0.001).mean()
e = Exchange(trade_symbols,initial_balance=10000,taker_fee=0.0002)
for row in df[trade_symbols].dropna().iterrows():
    date = row[0]
    prices = row[1]
    e.Update(date, trade_symbols, prices)
    account.append([e.account['USDT']['margin'],e.account['USDT']['realised_profit']+e.account['USDT']['unrealised_profit']])
    aim_amount = -round((diff[date] - diff_mean[date])/30,1)
    now_amount = e.account['BTCUSD_PERP']['amount']
    if aim_amount - now_amount < -1:
        trade_amount = now_amount - aim_amount
        e.Buy('BTCUSD_200925',prices['BTCUSD_200925'],2*trade_amount)
        e.Sell('BTCUSD_201225',prices['BTCUSD_201225'],trade_amount)
        e.Sell('BTCUSD_PERP',prices['BTCUSD_PERP'],trade_amount)
    if aim_amount - now_amount > 1:
        trade_amount = aim_amount - now_amount
        e.Sell('BTCUSD_200925',prices['BTCUSD_200925'],2*trade_amount)
        e.Buy('BTCUSD_201225',prices['BTCUSD_201225'],trade_amount)
        e.Buy('BTCUSD_PERP',prices['BTCUSD_PERP'],trade_amount)
    
e.df = pd.DataFrame(index=df[trade_symbols].dropna().index,columns=['margin','profit'],data=account)
e.df['profit'].plot(figsize=(15,6),grid=True);

À l'extérieur[39]:

img

Dans [59]:

symbol = 'ETH'
trade_symbols = [symbol+'USD_201225', symbol+'USD_200925', symbol+'USD_PERP']
fee = 0.0002
account = []
diff = df[trade_symbols[0]]+df[trade_symbols[2]]-2*df[trade_symbols[1]]
diff_mean = diff.ewm(alpha=0.001).mean()
e = Exchange(trade_symbols,initial_balance=10000,taker_fee=fee)
for row in df[trade_symbols].dropna().iloc[30:].iterrows():
    date = row[0]
    prices = row[1]
    e.Update(date, trade_symbols, prices)
    account.append([e.account['USDT']['margin'],e.account['USDT']['realised_profit']+e.account['USDT']['unrealised_profit']])
    aim_amount = -round((diff[date] - diff_mean[date])/(15*prices[trade_symbols[2]]*fee),1)
    now_amount = e.account[trade_symbols[2]]['amount']
    if aim_amount - now_amount < -1:
        trade_amount = 1
        e.Buy(trade_symbols[1],prices[trade_symbols[1]],2*trade_amount)
        e.Sell(trade_symbols[0],prices[trade_symbols[0]],trade_amount)
        e.Sell(trade_symbols[2],prices[trade_symbols[2]],trade_amount)
    if aim_amount - now_amount > 1:
        trade_amount = 1
        e.Sell(trade_symbols[1],prices[trade_symbols[1]],2*trade_amount)
        e.Buy(trade_symbols[0],prices[trade_symbols[0]],trade_amount)
        e.Buy(trade_symbols[2],prices[trade_symbols[2]],trade_amount)
e.df = pd.DataFrame(index=df[trade_symbols].dropna().iloc[30:].index,columns=['margin','profit'],data=account)
e.df['profit'].plot(figsize=(15,6),grid=True);

À l'extérieur[59]:

img

Dans [60]:

symbol = 'LINK'
trade_symbols = [symbol+'USD_201225', symbol+'USD_200925', symbol+'USD_PERP']
fee = 0.0002
account = []
diff = df[trade_symbols[0]]+df[trade_symbols[2]]-2*df[trade_symbols[1]]
diff_mean = diff.ewm(alpha=0.001).mean()
e = Exchange(trade_symbols,initial_balance=10000,taker_fee=fee)
for row in df[trade_symbols].dropna().iloc[30:].iterrows():
    date = row[0]
    prices = row[1]
    e.Update(date, trade_symbols, prices)
    account.append([e.account['USDT']['margin'],e.account['USDT']['realised_profit']+e.account['USDT']['unrealised_profit']])
    aim_amount = -round((diff[date] - diff_mean[date])/(15*prices[trade_symbols[2]]*fee),1)
    now_amount = e.account[trade_symbols[2]]['amount']
    if aim_amount - now_amount < -1:
        trade_amount = 1
        e.Buy(trade_symbols[1],prices[trade_symbols[1]],2*trade_amount)
        e.Sell(trade_symbols[0],prices[trade_symbols[0]],trade_amount)
        e.Sell(trade_symbols[2],prices[trade_symbols[2]],trade_amount)
    if aim_amount - now_amount > 1:
        trade_amount = 1
        e.Sell(trade_symbols[1],prices[trade_symbols[1]],2*trade_amount)
        e.Buy(trade_symbols[0],prices[trade_symbols[0]],trade_amount)
        e.Buy(trade_symbols[2],prices[trade_symbols[2]],trade_amount)
e.df = pd.DataFrame(index=df[trade_symbols].dropna().iloc[30:].index,columns=['margin','profit'],data=account)
e.df['profit'].plot(figsize=(15,6),grid=True);

Extrait[60]:

img

5.Sensibilité des commissions

Comme 3 contrats doivent être exploités en même temps, 8 frais de service sont nécessaires pour fermer la position après l'ouverture, de sorte que les frais de service ont un grand impact sur la stratégie.

img

Si la commission est de 0,03%, les résultats du backtest BTC sont les suivants:

img

Les résultats des tests antérieurs de l'ETH:

img

Le taux de prise de vip0 pour les nouveaux utilisateurs inscrits est de 0,0004, 10% sera réduit au premier mois d'invitation, 30% sera retourné et 10% sera réduit pour la consommation de BNB. Ainsi, les frais de traitement finaux sont de 0,0002268. Il y aura également une récompense directe pour le récent grand montant de transaction du contrat de livraison de Binance. En outre, une partie de la facture peut être placée et une partie de la facture peut être prise, et le taux global final peut être réduit à 0,02%.

Résumé

Le but de l'arbitrage est de trouver une différence de prix stable. La différence de prix de la différence de prix est plus stable. Par conséquent, l'arbitrage papillon est beaucoup moins risqué que la période croisée et le spot futur, et il peut également être opéré manuellement.

Dans [23]:

class Exchange:
    
    def __init__(self, trade_symbols, leverage=20, maker_fee=0.0002,taker_fee=0.0004,log='',initial_balance=10000):
        self.initial_balance = initial_balance #Initial assets
        self.taker_fee = taker_fee
        self.maker_fee = maker_fee
        self.leverage = leverage
        self.trade_symbols = trade_symbols
        self.date = ''
        self.log = log
        self.df = pd.DataFrame()
        self.account = {'USDT':{'realised_profit':0, 'margin':0, 'unrealised_profit':0, 
                                'total':initial_balance, 'leverage':0, 'fee':0,'maker_fee':0,'taker_fee':0}}
        for symbol in trade_symbols:
            self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0,
                                    'margin':0, 'unrealised_profit':0,'fee':0}
            
    def Trade(self, symbol, direction, price, amount, msg='', maker=True):
        
        if (self.date and symbol == self.log) or self.log == 'all':
            print('%-26s%-15s%-5s%-10.8s%-8.6s %s'%(str(self.date)[:24], symbol, 'buy' if direction == 1 else 'sell', price, amount, msg))

        cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
        open_amount = amount - cover_amount
        if maker:
            self.account['USDT']['realised_profit'] -= price*amount*self.maker_fee #Deduct service charge
            self.account['USDT']['maker_fee'] += price*amount*self.maker_fee
            self.account['USDT']['fee'] += price*amount*self.maker_fee
            self.account[symbol]['fee'] += price*amount*self.maker_fee
        else:
            self.account['USDT']['realised_profit'] -= price*amount*self.taker_fee #Deduct service charge
            self.account['USDT']['taker_fee'] += price*amount*self.taker_fee
            self.account['USDT']['fee'] += price*amount*self.taker_fee
            self.account[symbol]['fee'] += price*amount*self.taker_fee

        
        
        if cover_amount > 0: #Close the position first
            self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount  #Profit
            self.account['USDT']['margin'] -= cover_amount*self.account[symbol]['hold_price']/self.leverage #Release margin
            
            self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
            self.account[symbol]['amount'] -= -direction*cover_amount
            self.account[symbol]['margin'] -=  cover_amount*self.account[symbol]['hold_price']/self.leverage
            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['USDT']['margin'] +=  open_amount*price/self.leverage            
            self.account[symbol]['hold_price'] = total_cost/total_amount
            self.account[symbol]['amount'] += direction*open_amount
            self.account[symbol]['margin'] +=  open_amount*price/self.leverage
            
        self.account[symbol]['unrealised_profit'] = (price - self.account[symbol]['hold_price'])*self.account[symbol]['amount']
        self.account[symbol]['price'] = price
        self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*price
        
    
    def Buy(self, symbol, price, amount, msg='', maker=False):
        self.Trade(symbol, 1, price, amount, msg, maker)
        
    def Sell(self, symbol, price, amount, msg='', maker=False):
        self.Trade(symbol, -1, price, amount, msg,maker)
        

    def Update(self, date, symbols, close_price): #Update the assets
        self.date = date
        self.close = close_price
        self.account['USDT']['unrealised_profit'] = 0
        for symbol in 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)
        self.account['USDT']['leverage'] = round(self.account['USDT']['margin']*self.leverage/self.account['USDT']['total'],4)

Dans [ ]:


Relationnée

Plus de