В процессе загрузки ресурсов... загрузка...

[Binance Championship] Стратегия контракта поставки Binance 3 - хеджирование бабочек

Автор:FMZ~Lydia, Создано: 2021-09-01 18:17:46, Обновлено: 2023-09-14 20:32:10

img

Недавно фьючерсы Binance запустили второй Binance Championship (адрес:https://www.binancezh.com/cn/futures/activity/anniversary-competition/129-38599440FMZ Quant прямо. В настоящее время нас всего более 100 человек. Добро пожаловать к нам. После этого вы можете добавить WeChat лидера команды: fmz_zhangchao, ответить Binance, и мы пригласим вас присоединиться к группе WeChat.

Стратегия, подготовленная для чемпионата Binance, - это бабочка хеджирования контракта доставки. Эта статья представляет собой исследовательский отчет о стратегии. Обратите внимание: стратегии предназначены только для справки. Вы можете представить свои собственные идеи по оптимизации на этой основе. Вы также можете поделиться. Отчет можно использовать в исследовательской среде веб-сайта FMZ напрямую (нажмите в правом верхнем углу, чтобы скачать и загрузить в Analyze).

img

1. Стратегические причины

Хеджирование должно найти стабильную разницу в цене. Когда разница в цене слишком велика, короткий хеджирование ценовой разницы. Когда разница в цене слишком мала, длинный хеджирование ценовой разницы. Когда разница в цене возвращается, чтобы закрыть позицию, вы заработаете разницу в цене. Если фьючерсы и споты хеджируются, когда цена невыполненных фьючерсов намного выше, чем спотная цена, вы можете сделать короткий фьючерсный контракт и длинный хеджирование спотной цены, чтобы сократить разницу в цене. Есть также межвременные хеджирования контрактов с различными сроками доставки, с фьючерсами и спотами хеджирования, они также могут идти длинные ценовые различия. Фьючерсы и споты и кросс-фьючерсы являются распространенными стратегиями с ожесточенной конкуренцией. Когда нет рынка, разница в цене относительно стабильна. Хотя это может быть долгосро

Принципы стратегии

Стандартные валютные контракты Binance, такие как BTC и ETH, имеют три контракта одновременно, а именно, вечный BTCUSD_ PERP, BTCUSD_200925 текущего квартала, BTCUSD_ 201225 следующего квартала. Вечные контракты могут использоваться в качестве спотов. Как правило, для хеджирования двух контрактов существует три ценовых дифференциала: текущий квартал-вечный, следующий квартал-вечный и следующий квартал-сегодняшний. Арбитраж бабочки требует трех контрактов. Разница (следующий квартал - текущий квартал) - (текущий квартал - вечный), то есть разница = следующий квартал + вечный - 2 * текущий квартал. Чтобы увеличить разницу, вам нужно открыть длинную позицию контракта на следующий квартал и вечные контракты, и сделать два коротких контракта на текущий квартал.

3. Хеджирование пространства

Я просмотрел данные 5min K-линии Binance с 14 августа по 14 сентября, которые можно прочитать напрямую (из-за разницы во времени, показанная разница во времени составляет 8 часов).

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

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

Выход[12]:

img

Во-первых, давайте посмотрим на ценовую разницу между контрактами биткойна. 17 августа цена биткойна быстро выросла на 500u. В целом, контракт, поставленный, был на премию по сравнению со спотовой ценой, и спотовая цена выросла. Ожидания на будущее будут более оптимистичными. Ценовая разница между недоставленным контрактом и вечности станет больше. Например, разница между ценой следующего квартала и вечности составит 700u. С падением цены биткойна в сентябре ожидания людей быстро ухудшатся, ценовая разница между следующим кварталом и вечности упала примерно до 150u, и почти не было разницы в цене между текущим кварталом и вечностью. Если бы хеджирование между следующим кварталом и вечностью было проведено, можно было бы осуществить только возврат большой долгосрочной цены. Если разница между 400-600 августа была решена, чтобы быть проведенной в настоящее время, очевидно, она

В [18]:

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

Выход[18]:

img

В [15]:

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

Выход[15]:

img

В [16]:

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

Выход[16]:

img

В [17]:

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

Выход[17]:

img

Так как же изменяется разница в цене в это время? Как видно на рисунке ниже, недавняя разница в цене долгое время оставалась стабильной на уровне 100-200u. Даже резкое падение в начале сентября не сильно повлияло, что дало нам много возможностей для повторного арбитража. В настоящее время, если разница в цене падает до 100u, можно идти в длинный ручной.

Когда спотовая цена колеблется, два не истекших контракта одновременно отражают ожидания будущего. Процесс сокращения ценовой разницы может в значительной степени компенсировать это колебание, и производительность относительно стабильна.

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

Выход[19]:

img

В [22]:

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

Выход[22]:

img

4. Обратная проверка стратегии

Чтобы сэкономить время (просто ленивость), бэкстест по-прежнему использует стандартный двигатель USDT последней стратегии Binance Championship. Хотя могут быть некоторые ошибки, он также может объяснить проблему. Инженер бэкстестинга размещен в конце этого отчета. При запуске кода вы должны увидеть конец статьи. Стандартная стратегия валюты может рассмотреть хеджирование, если вы хотите заработать USDT, и это несложно.

Средняя линия ценовой разницы отслеживается EMA, а позиция контролируется сеткой, то есть каждый раз, когда открывается разница (например, 30), идет короткий N акций, и наоборот. Если средняя линия ценовой разницы составляет 100u, когда ценовая разница составляет 90, идет короткий 3 акции, и ценовая разница становится 60, закрыть одну акцию. Размер сетки является ключевым параметром.

Ниже приведены конкретные коды и результаты обратного тестирования BTC и ETH. Производительность соответствует ожиданиям. Поскольку ETH и LINK имеют большую волатильность и ценовая разница более стабильна, производительность лучше. Обратите внимание, что плата за обслуживание здесь составляет 0,02%, а плата за обслуживание пользователя VIP0 по умолчанию в Binance составляет 0,04%.

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

Выход[39]:

img

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

Выход[59]:

img

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

Выход[60]:

img

5.Чувствительность комиссии

Поскольку 3 контракта должны работать одновременно, для закрытия позиции после открытия требуется 8 сервисных сборов, поэтому сервисные сборы оказывают большое влияние на стратегию.

img

Если комиссия составляет 0,03%, результаты обратного теста BTC следующие:

img

Результаты обратных тестов ETH:

img

Так, финальная плата за обработку составляет 0,0002268. Также будет непосредственная награда за недавнюю крупную сумму транзакции контракта доставки Binance. Кроме того, часть счета может быть размещена и часть счета может быть взята, и окончательная общая ставка может быть снижена до 0,02%. Кроме того, чиновник FMZ также обсуждает с Binance вопрос о скидке сбора за обслуживание.

Резюме

Цель арбитража - найти стабильную ценовую разницу. Ценовая разница ценовой разницы более стабильна. Поэтому арбитраж бабочки гораздо менее рискован, чем кросс-период и фьючерс-спот, и его также можно управлять вручную. Эта стратегия служит только введением. При написании в реальном боте необходимо учитывать многие вопросы. Добро пожаловать всем к общению.

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

В [ ]:


Связанные

Больше