[Binance Championship] Binance Contract Delivery Estratégia 3 - Hedging Borboleta

Autora:Lydia., Criado: 2022-11-11 18:17:46, Atualizado: 2023-09-14 20:32:10

img

Recentemente, os futuros da Binance lançaram o segundo Binance Championship (endereço:https://www.binancezh.com/cn/futures/activity/anniversary-competition/129-38599440) A plataforma oficial FMZ Quant também organizou uma equipe, que pode ser encontrada pesquisando FMZ Quant diretamente. No momento, há pouco mais de 100 pessoas. Bem-vindo a nos juntar. Depois disso, você pode adicionar o WeChat do líder da equipe: fmz_zhangchao, responder Binance, e nós o convidaremos a se juntar ao grupo WeChat.

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 Analise).

img

1. Razões estratégicas

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.

2. Princípios da estratégia

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.

3. Espaço de cobertura

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]:

img

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]:

img

Em [15]:

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

Fora [1]:

img

Em [16]:

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

Fora [1]:

img

Em [17]:

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

Fora [1]:

img

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]:

img

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]:

img

4. Backtest da estratégia

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]:

img

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]:

img

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]:

img

5.Sensibilidade da comissão

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.

img

Se a comissão for de 0,03%, os resultados do backtest BTC são os seguintes:

img

Os resultados do backtest da ETH:

img

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.

Resumo

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 [ ]:


Relacionados

Mais.