Vor kurzem startete Binance Futures die zweite
Die Strategie, die für die Binance Championship vorbereitet wurde, ist die Schmetterlingssicherung des Liefervertrags. Dieser Artikel ist der Forschungsbericht der Strategie. Achtung: Strategien dienen nur zur Referenz. Sie können Ihre eigenen Ideen für die Optimierung auf dieser Grundlage vorlegen. Sie sind auch herzlich eingeladen zu teilen. Der Bericht kann direkt in der Forschungsumgebung der FMZ-Website verwendet werden (klicken Sie in der oberen rechten Ecke zum Herunterladen und laden Sie in der
Der Hedging muss eine stabile Preisdifferenz finden. Wenn die Preisdifferenz zu groß ist, kann man die Preisdifferenz auch abschneiden. Wenn die Preisdifferenz zu klein ist, geht man lang die Preisdifferenz. Wenn die Preisdifferenz zurückkehrt, um die Position zu schließen, verdient man die Preisdifferenz. Wenn die Futures und Spots abgeschirmt werden, wenn der Preis der nicht ausgelieferten Futures viel höher ist als der Spotpreis, kann man den Futures-Kontrakt kurz gehen und den Spotpreis lang gehen, um die Preisdifferenz zu kürzen. Es gibt auch intertemporalen Hedges von Verträgen mit unterschiedlichen Lieferzeiten, mit Futures und Spots Hedges, sie können auch lange Preisdifferenzen gehen. Futures und Spots und Cross-Futures sind gemeinsame Strategien mit heftigem Wettbewerb. Wenn es keinen Markt gibt, ist die Preisdifferenz relativ stabil. Obwohl es ein langfristiger Markt sein kann, gibt es nur wenige Möglichkeiten, und man sucht auch nach manuellen Operationen. Da es alle drei Differenzen
Die Binance-Währungsstandardkontrakte, wie BTC und ETH, haben drei Kontrakte gleichzeitig, nämlich perpetual BTCUSD_ PERP, BTCUSD_200925 des laufenden Quartals, BTCUSD_ 201225 des nächsten Quartals. Perpetual-Kontrakte können als Spots verwendet werden. Im Allgemeinen gibt es drei Preisdifferenzen zur Absicherung von zwei Kontrakte: aktuelles Quartal-perpetual, nächstes Quartal-perpetual und nächstes Quartal-aktuelles Quartal. Butterfly-Arbitrage erfordert drei Kontrakte. Der Unterschied ist (nächstes Quartal - aktuelles Quartal) - (aktuelles Quartal - perpetual), dh der Unterschied = nächstes Quartal + perpetual - 2 * aktuelles Quartal. Um den Unterschied zu verlängern, müssen Sie einen Long-Position-Kontrakt für das nächste Quartal und perpetual-Kontrakte eröffnen und zwei Short-Kontrakte für das aktuelle Quartal eröffnen.
Ich habe die Daten von 5min K-Line von Binance vom 14. August bis zum 14. September durchsucht, die direkt gelesen werden können (aufgrund des Zeitunterschieds ist der zeitliche Unterschied 8h).
In [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
In [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)
Ausgeschaltet:
Zuerst werfen wir einen Blick auf die Preisunterschiede zwischen Bitcoin-Kontrakten. Am 17. August stieg der Preis von Bitcoin schnell um 500u. Im Allgemeinen war der ausgelieferte Vertrag im Vergleich zum Spotpreis zu einem Plus, und der Spotpreis stieg. Die Erwartung für die Zukunft wird optimistischer sein. Die Preisunterschiede zwischen dem nicht ausgelieferten Vertrag und der Perpetuität wird größer. Zum Beispiel wird die Preisunterschiede zwischen dem nächsten Quartal und der Perpetuität 700u betragen. Mit der Abnahme des Bitcoin-Preises im September werden sich die Erwartungen der Leute schnell verschlechtern, die Preisunterschiede zwischen dem nächsten Quartal und der Perpetuität fiel auf etwa 150u, und es gab fast keinen Preisunterschied zwischen dem aktuellen Quartal und der Perpetuität. Wenn die Absicherung zwischen dem nächsten Quartal und der Perpetuität durchgeführt wurde, könnte nur die Rückkehr des großen langfristigen Preises durchgeführt werden. Wenn die Differenz zwischen 400-600 August beschlossen wurde, im August durchgeführt zu werden, ist sie offensichtlich in einem Zust
In [18]:
#Perpetual price
df['BTCUSD_PERP'].dropna().plot(figsize=(15,6),grid=True);
Ausgeschaltet[1]:
In [15]:
# Price difference of next quarter - perpetual
(df['BTCUSD_201225']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Ausgeschaltet[1]:
In [16]:
# Price difference of current quarter - perpetual
(df['BTCUSD_200925']-df['BTCUSD_PERP']).dropna().plot(figsize=(15,6),grid=True);
Ausgeschaltet [1]:
In [17]:
# Price difference of next quarter - current quarter
(df['BTCUSD_201225']-df['BTCUSD_200925']).dropna().plot(figsize=(15,6),grid=True);
Ausgeschaltet [1]:
Wie ändert sich die Preisdifferenz zu diesem Zeitpunkt? Wie aus der Abbildung unten zu sehen ist, ist die jüngste Preisdifferenz seit langem bei 100-200u stabil. Selbst der starke Rückgang Anfang September hat nicht viel beeinflusst, was uns viel Raum für wiederholte Arbitrage gibt.
Wenn der Spotpreis schwankt, spiegeln die beiden nicht abgelaufenen Kontrakte gleichzeitig die Erwartung der Zukunft wider. Der Prozess der Preisdifferenzreduktion kann diese Schwankung weitgehend ausgleichen, und die Performance ist relativ stabil.
In [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);
Ausgeschaltet[1]:
In [22]:
#The price difference of ETH
(df['ETHUSD_201225']+df['ETHUSD_PERP']-2*df['ETHUSD_200925']).dropna().plot(figsize=(15,6),grid=True);
Außen [1]:
Um Zeit zu sparen (nur Faulheit), verwendet der Backtest immer noch die USDT-Standard-Engine der letzten Binance Championship-Strategie. Obwohl es einige Fehler geben kann, kann er auch das Problem erklären. Die Backtesting-Engine wird am Ende dieses Berichts platziert. Wenn Sie den Code ausführen, sollten Sie das Ende des Artikels sehen. Die Währungsstandardstrategie kann Hedging in Betracht ziehen, wenn Sie USDT verdienen möchten, und es ist nicht kompliziert.
Die Mittellinie der Preisdifferenz wird von der EMA verfolgt, und die Position wird durch das Gitter gesteuert, d.h. jedes Mal, wenn die Differenz geöffnet wird (z.B. 30), gehen Sie kurz N Aktien und umgekehrt. Wenn die Mittellinie der Preisdifferenz 100u beträgt, wenn die Preisdifferenz 90 beträgt, gehen Sie kurz 3 Aktien, und die Preisdifferenz wird 60, schließen Sie eine Aktie. Die Größe des Gitter ist ein wichtiger Parameter.
Hier sind die spezifischen BTC- und ETH-Backtest-Codes und -Ergebnisse. Die Leistung entspricht den Erwartungen. Da ETH und LINK eine größere Volatilität aufweisen und der Preisunterschied stabiler ist, ist die Leistung besser. Beachten Sie, dass die Servicegebühr hier 0,02%, und die Standard-VIP0-Nutzer-Servicegebühr in Binance 0,04%. Die Servicegebühr ist sehr wichtig, und die folgenden Kapitel werden sie analysieren.
In [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);
Außen [1]:
In [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);
Aus [59]:
In [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);
Ausgeschaltet[60]:
Da 3 Verträge gleichzeitig betrieben werden müssen, sind 8 Servicegebühren erforderlich, um die Position nach der Eröffnung zu schließen, so dass die Servicegebühren einen großen Einfluss auf die Strategie haben.
Bei einer Provision von 0,03% sind die Ergebnisse des BTC-Backtests wie folgt:
Die Ergebnisse des Backtests von ETH:
Der Taker-Rate von vip0 für neue registrierte Benutzer beträgt 0,0004, 10% wird im ersten Monat der Einladung reduziert, 30% wird zurückgegeben und 10% wird für den BNB-Verbrauch reduziert. Somit beträgt die endgültige Bearbeitungsgebühr 0,0002268. Es wird auch eine direkte Belohnung für den jüngsten großen Transaktionsbetrag des Binance-Liefervertrags geben. Darüber hinaus kann ein Teil der Rechnung platziert und ein Teil der Rechnung entgegengenommen werden, und der endgültige Gesamtsatz kann auf 0,02 reduziert werden. Darüber hinaus diskutiert der FMZ-Beamte auch mit Binance über die Frage der Dienstleistungsrabattung.
Der Zweck der Arbitrage ist es, einen stabilen Preisunterschied zu finden. Der Preisunterschied des Preisunterschieds ist stabiler. Daher ist der Schmetterlingsarbitrage viel weniger riskant als Cross-Periode und Future-Spot, und er kann auch manuell betrieben werden. Diese Strategie dient nur als Einführung. Bei der Erstellung im echten Bot müssen viele Fragen berücksichtigt werden.
In [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)
In [ ]: