[Campeonato de Binance] Estrategia de contrato de entrega de Binance 3 - Cobertura de mariposas

El autor:- ¿ Por qué?, Creado: 2022-11-11 18:17:46, Actualizado: 2023-09-14 20:32:10

img

Recientemente, los futuros de Binance lanzaron el segundo Binance Championship (dirección:https://www.binancezh.com/cn/futures/activity/anniversary-competition/129-38599440La plataforma oficial FMZ Quant también ha organizado un equipo, que se puede encontrar buscando FMZ Quant directamente. En la actualidad, hay poco más de 100 personas. Bienvenido a unirse a nosotros. Después de eso, puede agregar el WeChat del líder del equipo: fmz_zhangchao, responder Binance, y le invitaremos a unirse al grupo WeChat.

La estrategia preparada para el Campeonato de Binance es la cobertura de mariposa del contrato de entrega. Este artículo es el informe de investigación de la estrategia. Atención: las estrategias son solo para referencia. Puede presentar sus propias ideas para la optimización sobre esta base. También es bienvenido a compartir. El informe se puede usar en el entorno de investigación del sitio web de FMZ directamente (haga clic en la esquina superior derecha para descargar y cargar en el Análisis).

img

1. Razones estratégicas

El hedging necesita encontrar una diferencia de precio estable. Cuando la diferencia de precio es demasiado grande, corta la diferencia de precio. Cuando la diferencia de precio es demasiado pequeña, corta la diferencia de precio. Cuando la diferencia de precio regresa para cerrar la posición, ganará la diferencia de precio. Si los futuros y los puntos están cubiertos, cuando el precio de los futuros no entregados es mucho más alto que el precio spot, puede corto el contrato de futuros e ir largo el precio spot para corto la diferencia de precio. También hay cobertura intertemporal de contratos con diferentes tiempos de entrega, con futuros y hedges de puntos, también pueden ir largas diferencias de precio. Los futuros y los puntos y los futuros cruzados son estrategias comunes con una competencia feroz. Cuando no hay mercado, la diferencia de precio es relativamente estable. Aunque puede ser un mercado a largo plazo, hay pocas oportunidades, y también se está buscando una operación manual.

2. Principios de la estrategia

Los contratos estándar de moneda de Binance, como BTC y ETH, tienen tres contratos al mismo tiempo, a saber, perpetual BTCUSD_ PERP, BTCUSD_200925 del trimestre actual, BTCUSD_ 201225 del trimestre siguiente. Los contratos perpetuos se pueden usar como puntos. En general, hay tres diferenciales de precio para cubrir dos contratos: actual trimestre perpetuo, próximo trimestre perpetuo y próximo trimestre actual. El arbitraje mariposa requiere tres contratos. La diferencia es (próximo trimestre - trimestre actual) - (actual trimestre - perpetuo), es decir, la diferencia = próximo trimestre + perpetuo - 2 * trimestre actual. Para ir largo la diferencia, debe abrir un contrato de posición larga para el próximo trimestre y los contratos perpetuos, y ir corto dos contratos para el trimestre actual.

3. Espacio de cobertura

He rastreado los datos de 5min K-line de Binance del 14 de agosto al 14 de septiembre, que se pueden leer directamente (debido a la diferencia horaria, la diferencia horaria mostrada es de 8h).

En [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

En [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)

Fuera [12]:

img

Primero, echemos un vistazo a la diferencia de precio entre los contratos de Bitcoin. El 17 de agosto, el precio de Bitcoin aumentó rápidamente en 500 u. En términos generales, el contrato entregado tenía una prima en comparación con el precio al contado, y el precio al contado aumentó. La expectativa para el futuro será más optimista. La diferencia de precio entre el contrato no entregado y la perpetuidad se hará más grande. Por ejemplo, la diferencia de precio entre el próximo trimestre y la perpetuidad será de 700 u. Con la disminución del precio de Bitcoin en septiembre, las expectativas de la gente se deteriorarán rápidamente, la diferencia de precio entre el próximo trimestre y la perpetuidad cayó a alrededor de 150 u, y casi no había diferencia de precio entre el trimestre actual y la perpetuidad. Si se llevó a cabo la cobertura entre el próximo trimestre y la perpetuidad, solo se podría llevar a cabo el retorno de un gran precio a largo plazo. Si se decidió llevar a cabo la diferencia de precio entre 400-600 de agosto, obviamente ahora está bloqueada en un estado.

En el [18]:

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

Fuera [1]:

img

En [15]:

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

Fuera [1]:

img

En [16]:

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

Fuera [1]:

img

En [17]:

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

Fuera [1]:

img

Entonces, ¿cómo cambia la diferencia de precio en este momento? Como se puede ver en la figura a continuación, la diferencia de precio reciente ha sido estable en 100-200u durante mucho tiempo. Incluso la fuerte caída a principios de septiembre no afectó mucho, lo que nos da mucho espacio para el arbitraje repetido. En la actualidad, si la diferencia de precio cae a 100u, está bien ir largo manualmente.

Cuando el precio al contado fluctúa, los dos contratos sin vencimiento reflejan la expectativa del futuro al mismo tiempo. El proceso de reducción de la diferencia de precios puede compensar esta fluctuación en gran medida, y el rendimiento es relativamente estable.

En [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);

Fuera [1]:

img

En [22]:

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

Fuera[22]:

img

4. Prueba posterior de la estrategia

Para ahorrar tiempo (solo pereza), el backtest todavía utiliza el motor estándar USDT de la última estrategia de Binance Championship. Aunque puede haber algunos errores, también puede explicar el problema. El motor de backtesting se coloca al final de este informe. Al ejecutar el código, debe ver el final del artículo. La estrategia estándar de divisas puede considerar la cobertura si desea ganar USDT, y no es complicado.

La línea media de la diferencia de precio es rastreada por la EMA, y la posición es controlada por la cuadrícula, es decir, cada vez que se abre la diferencia (como 30), se corta N acciones, y viceversa. Si la línea media de la diferencia de precio es 100u, cuando la diferencia de precio es 90, se corta 3 acciones, y la diferencia de precio se convierte en 60, cierra una acción.

Los siguientes son los códigos y resultados específicos de backtesting de BTC y ETH. El rendimiento está en línea con las expectativas. Debido a que ETH y LINK tienen una mayor volatilidad y la diferencia de precio es más estable, el rendimiento es mejor. Tenga en cuenta que el cargo por servicio aquí es del 0.02%, y el cargo por servicio por defecto de los tomadores de vip0 en Binance es del 0.04%. El cargo por servicio es muy importante, y los siguientes capítulos lo analizarán.

En [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);

Fuera[39]:

img

En [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);

Fuera[59]:

img

En el [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);

Fuera de juego[60]:

img

5.Sensibilidad de las comisiones

Dado que 3 contratos deben operarse al mismo tiempo, se requieren 8 cargos de servicio para cerrar la posición después de la apertura, por lo que los cargos de servicio tienen un gran impacto en la estrategia.

img

Si la comisión es del 0,03%, los resultados de las pruebas de retroceso de BTC son los siguientes:

img

Los resultados de las pruebas de retroceso de ETH:

img

La tasa de recepción de vip0 para los nuevos usuarios registrados es de 0.0004, se reducirá un 10% en el primer mes de ser invitados, se devolverá un 30% y se reducirá un 10% para el consumo de BNB. Por lo tanto, la tarifa de manejo final es de 0.0002268. También habrá una recompensa directa por el reciente gran monto de transacción del contrato de entrega de Binance. Además, parte de la factura se puede colocar y parte de la factura se puede tomar, y la tasa integral final se puede reducir a 0.02%. Además, el funcionario de FMZ también está discutiendo el tema del descuento de los cargos de servicio con Binance. Puedes esperarlo.

Resumen de las actividades

El objetivo del arbitraje es encontrar una diferencia de precio estable. La diferencia de precio de la diferencia de precio es más estable. Por lo tanto, el arbitraje mariposa es mucho menos riesgoso que el período cruzado y el futuro, y también se puede operar manualmente. Esta estrategia solo sirve como introducción. Muchos problemas deben considerarse al escribir en el bot real.

En [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)

En [ ]:


Relacionados

Más.