Recentemente, os futuros da Binance lançaram o segundo
A estratégia preparada para o Campeonato Binance é a cobertura de borboleta do contrato de entrega. Este artigo é o relatório de pesquisa da estratégia. Atenção: as estratégias são apenas para referência. Você pode apresentar suas próprias ideias para otimização nesta base. Você também é bem-vindo a compartilhar. O relatório pode ser usado no ambiente de pesquisa do site FMZ diretamente (clique no canto superior direito para baixar e carregue no
O hedging precisa encontrar uma diferença de preço estável. Quando a diferença de preço é muito grande, fazer curto a diferença de preço. Quando a diferença de preço é muito pequena, fazer longo a diferença de preço. Quando a diferença de preço retorna para fechar a posição, você ganhará a diferença de preço. Se os futuros e os spots estão sendo hedged, quando o preço dos futuros não entregues é muito maior do que o preço do ponto, você pode fazer curto o contrato de futuros e fazer longo o preço do ponto para fazer curto a diferença de preço. Há também hedges intertemporais de contratos com diferentes tempos de entrega, com hedges de futuros e spots, eles também podem fazer longos os preços. Os futuros e os spots e os cross-futures são estratégias comuns com uma concorrência feroz. Quando não há mercado, a diferença de preço é relativamente estável. Embora possa ser um mercado de longo prazo, há poucas oportunidades, e a operação manual também está procurando.
Os contratos padrão de moeda da Binance, como BTC e ETH, têm três contratos ao mesmo tempo, a saber, BTCUSD_ PERP perpétuo, BTCUSD_200925 do trimestre atual, BTCUSD_ 201225 do próximo trimestre. Os contratos perpétuos podem ser usados como pontos. Geralmente, existem três diferenciais de preço para cobrir dois contratos: trimestre atual perpétuo, próximo trimestre perpétuo e próximo trimestre atual. A arbitragem de borboleta requer três contratos. A diferença é (próximo trimestre - trimestre atual) - (quarto atual - perpétuo), ou seja, a diferença = próximo trimestre + perpétuo - 2 * trimestre atual.
Eu rastreei os dados de 5min K-line da Binance de 14 de agosto a 14 de setembro, que podem ser lidos diretamente (devido à diferença horária, a diferença horária mostrada é de 8h).
Em [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
Em [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)
Fora[12]:
Primeiro, vamos dar uma olhada na diferença de preço entre os contratos do Bitcoin. Em 17 de agosto, o preço do Bitcoin subiu rapidamente em 500u. Em geral, o contrato entregue foi em um prêmio em comparação com o preço ao instante, e o preço ao instante subiu. A expectativa para o futuro será mais otimista. A diferença de preço entre o contrato não entregue e a perpetuidade se tornará maior. Por exemplo, a diferença de preço entre o próximo trimestre e a perpetuidade será de 700u. Com a diminuição do preço do Bitcoin em setembro, as expectativas das pessoas se deteriorarão rapidamente, a diferença de preço entre o próximo trimestre e a perpetuidade caiu para cerca de 150u, e quase não houve diferença de preço entre o trimestre atual e a perpetuidade. Se a cobertura entre o próximo trimestre e a perpetuidade fosse realizada, apenas o retorno de um grande preço a longo prazo poderia ser realizado. Se a diferença entre 400-600 de agosto fosse decidida a ser realizada agora, obviamente está bloqueada em um estado.
Em [18]:
#Perpetual price
df['BTCUSD_PERP'].dropna().plot(figsize=(15,6),grid=True);
Fora [1]:
Em [15]:
# Price difference of next quarter - perpetual
(df['BTCUSD_201225']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Fora [1]:
Em [16]:
# Price difference of current quarter - perpetual
(df['BTCUSD_200925']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Fora [1]:
Em [17]:
# Price difference of next quarter - current quarter
(df['BTCUSD_201225']-df['BTCUSD_200925']).dropna().plot(figsize=(15,6),grid=True);
Fora [1]:
Então, como a diferença de preço muda neste momento? Como pode ser visto na figura abaixo, a diferença de preço recente tem sido estável em 100-200u por um longo tempo. Mesmo a queda acentuada no início de setembro não afetou muito, dando-nos muito espaço para arbitragem repetida.
Quando o preço ao instante flutua, os dois contratos não expirados refletem a expectativa do futuro ao mesmo tempo. O processo de redução da diferença de preço pode compensar essa flutuação em grande parte, e o desempenho é relativamente estável.
Em [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);
Fora [1]:
Em [22]:
#The price difference of ETH
(df['ETHUSD_201225']+df['ETHUSD_PERP']-2*df['ETHUSD_200925']).dropna().plot(figsize=(15,6),grid=True);
Fora[22]:
Para economizar tempo (apenas preguiça), o backtest ainda usa o mecanismo padrão USDT da última estratégia do Campeonato Binance. Embora possa haver alguns erros, ele também pode explicar o problema. O mecanismo de backtesting é colocado no final deste relatório. Ao executar o código, você deve ver o final do artigo. A estratégia padrão de moeda pode considerar a cobertura se você quiser ganhar USDT, e não é complicado.
A linha média da diferença de preço é rastreada pela EMA, e a posição é controlada pela grade, ou seja, toda vez que a diferença é aberta (como 30), vá curto N ações, e vice-versa.
Os seguintes são os códigos e resultados específicos de backtesting do BTC e do ETH. O desempenho está de acordo com as expectativas. Como o ETH e o LINK têm maior volatilidade e a diferença de preço é mais estável, o desempenho é melhor. Observe que a taxa de serviço aqui é de 0,02%, e a taxa de serviço padrão do vip0 no Binance é de 0,04%. A taxa de serviço é muito importante e os capítulos seguintes irão analisá-la.
Em [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);
Fora[39]:
Em [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);
Fora[59]:
Em [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);
Fora[60]:
Como 3 contratos precisam ser operados ao mesmo tempo, 8 taxas de serviço são necessárias para fechar a posição após a abertura, de modo que as taxas de serviço têm um grande impacto na estratégia.
Se a comissão for de 0,03%, os resultados do backtest BTC são os seguintes:
Os resultados do backtest da ETH:
A taxa de aceitação de vip0 para novos usuários registrados é de 0,0004, 10% será reduzida no primeiro mês de serem convidados, 30% será devolvido e 10% será reduzido para o consumo de BNB. Assim, a taxa de manipulação final é de 0,0002268. Também haverá uma recompensa direta pelo recente grande valor da transação do contrato de entrega do Binance. Além disso, parte da conta pode ser colocada e parte da conta pode ser tomada, e a taxa global final pode ser reduzida para 0,02%. Além disso, o funcionário da FMZ também está discutindo a questão do desconto de cobrança de serviço com o Binance. Você pode esperar por isso.
O objetivo da arbitragem é encontrar uma diferença de preço estável. A diferença de preço da diferença de preço é mais estável. Portanto, a arbitragem borboleta é muito menos arriscada do que o período cruzado e o spot futuro, e também pode ser operada manualmente. Esta estratégia serve apenas como uma introdução. Muitas questões precisam ser consideradas ao escrever no bot real.
Em [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)
Em [ ]: