Récemment, les contrats à terme Binance ont lancé le deuxième
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).
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.
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.
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]:
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]:
Dans [15]:
# Price difference of next quarter - perpetual
(df['BTCUSD_201225']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Extrait [1]:
Dans [16]:
# Price difference of current quarter - perpetual
(df['BTCUSD_200925']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Extrait [1]:
Dans [17]:
# Price difference of next quarter - current quarter
(df['BTCUSD_201225']-df['BTCUSD_200925']).dropna().plot(figsize=(15,6),grid=True);
Extrait [1]:
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]:
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]:
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]:
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]:
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]:
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.
Si la commission est de 0,03%, les résultats du backtest BTC sont les suivants:
Les résultats des tests antérieurs de l'ETH:
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%.
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 [ ]: