Vor kurzem sah ich das Quantitative Diary von BuOu
Der digitale Währungspaarhandel ist eine Handelsstrategie, die auf statistischer Arbitrage basiert, bei der gleichzeitig zwei stark korrelierte Kryptowährungen gekauft und verkauft werden, um Gewinne aus Preisabweichungen zu erzielen.
Die Pair-Trading-Strategien beruhen auf der historischen Korrelation zwischen den Preisen zweier digitaler Währungen. Wenn die Preise zweier Währungen eine starke Korrelation aufweisen, sind ihre Preistrends im Allgemeinen synchronisiert. Wenn sich das Preisverhältnis zwischen den beiden zu einem bestimmten Zeitpunkt erheblich abweist, kann dies als vorübergehende Anomalie angesehen werden und der Preis wird dazu neigen, auf normale Niveaus zurückzukehren. Der digitale Währungsmarkt ist stark miteinander verbunden. Wenn eine große digitale Währung (wie Bitcoin) erheblich schwankt, löst sie in der Regel eine koordinierte Reaktion in anderen digitalen Währungen aus. Einige Währungen können eine sehr offensichtliche positive Korrelation haben, die aufgrund der gleichen Investitionsinstitutionen, der gleichen Marktmacher und der gleichen Spur andauern kann. Einige Währungen sind negativ korreliert, aber es gibt weniger negativ korrelierte Währungen, und da sie alle vom Markttrend beeinflusst werden, haben sie oft konsistente Markttrends.
Nehmen wir an, dass Währung A und Währung B eine hohe Preiskorrelation aufweisen. Zu einem bestimmten Zeitpunkt beträgt der Durchschnittswert des A/B-Preisverhältnisses 1. Wenn sich das A/B-Preisverhältnis zu einem bestimmten Zeitpunkt um mehr als 0,001, d. h. mehr als 1,001, abweist, dann können Sie auf folgende Weise handeln: Öffnen Sie eine Long-Position bei B und eine Short-Position bei A. Im Gegenteil, wenn das A/B-Preisverhältnis unter 0,999 liegt: Öffnen Sie eine Long-Position bei A und öffnen Sie eine Short-Position bei B.
Der Schlüssel zur Rentabilität liegt in den Spread-Gewinnen, wenn die Preise vom Durchschnitt abweichen und wieder normal werden.
Diese Codes können direkt verwendet werden. Am besten ist es, Anancoda herunterzuladen und in Jupyer Notebook zu debuggen. Es enthält Pakete für die häufig verwendete Datenanalyse direkt.
import requests
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests, zipfile, io
%matplotlib inline
Info = requests.get('https://fapi.binance.com/fapi/v1/exchangeInfo')
b_symbols = [s['symbol'] for s in Info.json()['symbols'] if s['contractType'] == 'PERPETUAL' and s['status'] == 'TRADING' and s['quoteAsset'] == 'USDT']
b_symbols = list(filter(lambda x: x[-4:] == 'USDT', [s.split('_')[0] for s in b_symbols]))
b_symbols = [x[:-4] for x in b_symbols]
print(b_symbols) # Get all trading pairs being traded
Die Hauptfunktion der GetKlines-Funktion besteht darin, die historischen K-Line-Daten des angegebenen Handelspaar-Perpetual-Kontrakts von der Binance-Börse zu erhalten und die Daten in einem Pandas DataFrame zu speichern. Die K-Line-Daten enthalten Informationen wie Eröffnungspreis, höchster Preis, niedrigster Preis, Schlusskurs und Handelsvolumen. Diesmal verwenden wir hauptsächlich die Schlusskursdaten.
def GetKlines(symbol='BTCUSDT',start='2020-8-10',end='2024-7-01',period='1h',base='fapi',v = 'v1'):
Klines = []
start_time = int(time.mktime(datetime.strptime(start, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000
end_time = min(int(time.mktime(datetime.strptime(end, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000,time.time()*1000)
intervel_map = {'m':60*1000,'h':60*60*1000,'d':24*60*60*1000}
while start_time < end_time:
time.sleep(0.3)
mid_time = start_time+1000*int(period[:-1])*intervel_map[period[-1]]
url = 'https://'+base+'.binance.com/'+base+'/'+v+'/klines?symbol=%s&interval=%s&startTime=%s&endTime=%s&limit=1000'%(symbol,period,start_time,mid_time)
res = requests.get(url)
res_list = res.json()
if type(res_list) == list and len(res_list) > 0:
start_time = res_list[-1][0]+int(period[:-1])*intervel_map[period[-1]]
Klines += res_list
if type(res_list) == list and len(res_list) == 0:
start_time = start_time+1000*int(period[:-1])*intervel_map[period[-1]]
if mid_time >= end_time:
break
df = pd.DataFrame(Klines,columns=['time','open','high','low','close','amount','end_time','volume','count','buy_amount','buy_volume','null']).astype('float')
df.index = pd.to_datetime(df.time,unit='ms')
return df
Das Datenvolumen ist relativ groß. Um schneller herunterzuladen, werden nur die stündlichen K-Line-Daten der letzten drei Monate erhalten. df_close enthält die Schlusskursdaten aller Währungen.
start_date = '2024-04-01'
end_date = '2024-07-05'
period = '1h'
df_dict = {}
for symbol in b_symbols:
print(symbol)
if symbol in df_dict.keys():
continue
df_s = GetKlines(symbol=symbol+'USDT',start=start_date,end=end_date,period=period)
if not df_s.empty:
df_dict[symbol] = df_s
df_close = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
for symbol in symbols:
df_close[symbol] = df_dict[symbol].close
df_close = df_close.dropna(how='all')
Wir definieren ein Austauschobjekt für den folgenden Backtest.
class Exchange:
def __init__(self, trade_symbols, fee=0.0002, initial_balance=10000):
self.initial_balance = initial_balance #Initial assets
self.fee = fee
self.trade_symbols = trade_symbols
self.account = {'USDT':{'realised_profit':0, 'unrealised_profit':0, 'total':initial_balance,
'fee':0, 'leverage':0, 'hold':0, 'long':0, 'short':0}}
for symbol in trade_symbols:
self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0,'unrealised_profit':0,'fee':0}
def Trade(self, symbol, direction, price, amount):
cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
open_amount = amount - cover_amount
self.account['USDT']['realised_profit'] -= price*amount*self.fee #Deduction fee
self.account['USDT']['fee'] += price*amount*self.fee
self.account[symbol]['fee'] += price*amount*self.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[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
self.account[symbol]['amount'] -= -direction*cover_amount
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[symbol]['hold_price'] = total_cost/total_amount
self.account[symbol]['amount'] += direction*open_amount
def Buy(self, symbol, price, amount):
self.Trade(symbol, 1, price, amount)
def Sell(self, symbol, price, amount):
self.Trade(symbol, -1, price, amount)
def Update(self, close_price): #Update the assets
self.account['USDT']['unrealised_profit'] = 0
self.account['USDT']['hold'] = 0
self.account['USDT']['long'] = 0
self.account['USDT']['short'] = 0
for symbol in self.trade_symbols:
if not np.isnan(close_price[symbol]):
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'] = self.account[symbol]['amount']*close_price[symbol]
if self.account[symbol]['amount'] > 0:
self.account['USDT']['long'] += self.account[symbol]['value']
if self.account[symbol]['amount'] < 0:
self.account['USDT']['short'] += self.account[symbol]['value']
self.account['USDT']['hold'] += abs(self.account[symbol]['value'])
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']['hold']/self.account['USDT']['total'],3)
Die Korrelationsberechnung ist eine Methode in der Statistik, die zur Messung der linearen Beziehung zwischen zwei Variablen verwendet wird. Die am häufigsten verwendete Korrelationsberechnungsmethode ist der Pearson-Korrelationskoeffizient. Folgendes ist das Prinzip, die Formel und die Implementierungsmethode der Korrelationsberechnung. Der Pearson-Korrelationskoeffizient wird verwendet, um die lineare Beziehung zwischen zwei Variablen zu messen, und sein Wertebereich liegt zwischen -1 und 1:
Der Pearson-Korrelationskoeffizient bestimmt die Korrelation zwischen zwei Variablen durch Berechnung ihrer Kovarianz und Standardabweichung.
in denen:
Natürlich müssen Sie sich nicht zu sehr darum kümmern, wie es berechnet wird. Sie können 1 Zeile Code in Python verwenden, um die Korrelation aller Währungen zu berechnen. Die Abbildung zeigt eine Korrelationswärmekarte. Rot repräsentiert positive Korrelation, Blau repräsentiert negative Korrelation, und je dunkler die Farbe, desto stärker die Korrelation. Sie können sehen, dass der größte Teil des Bereichs dunkelrot ist, so dass die positive Korrelation digitaler Währungen sehr stark ist.
import seaborn as sns
corr = df_close.corr()
plt.figure(figsize=(20, 20))
sns.heatmap(corr, annot=False, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Heatmap of Cryptocurrency Closing Prices', fontsize=20);
Auf der Grundlage der Korrelation werden die 20 am häufigsten korrelierten Währungspaare ausgewählt.
MANA SAND 0.996562
ICX ZIL 0.996000
STORJ FLOW 0.994193
FLOW SXP 0.993861
STORJ SXP 0.993822
IOTA ZIL 0.993204
SAND 0.993095
KAVA SAND 0.992303
ZIL SXP 0.992285
SAND 0.992103
DYDX ZIL 0.992053
DENT REEF 0.991789
RDNT MANTA 0.991690
STMX STORJ 0.991222
BIGTIME ACE 0.990987
RDNT HOOK 0.990718
IOST GAS 0.990643
ZIL HOOK 0.990576
MATIC FLOW 0.990564
MANTA HOOK 0.990563
Der entsprechende Code lautet:
corr_pairs = corr.unstack()
# Remove self-correlation (i.e. values on the diagonal)
corr_pairs = corr_pairs[corr_pairs != 1]
sorted_corr_pairs = corr_pairs.sort_values(kind="quicksort")
# Extract the top 20 most and least correlated currency pairs
most_correlated = sorted_corr_pairs.tail(40)[::-2]
print("The top 20 most correlated currency pairs are:")
print(most_correlated)
Der spezifische Backtest-Code ist wie folgt. Die Demonstrationsstrategie beobachtet hauptsächlich das Preisverhältnis von zwei Kryptowährungen (IOTA und ZIL) und handelt entsprechend den Änderungen dieses Verhältnisses. Die spezifischen Schritte sind wie folgt:
e
mit einem Anfangsguthaben von $10.000 und einer Transaktionsgebühr von 0.02%.avg
.value = 1000
.df_close
.diff
.aim_value
Die Kauf- und Verkaufsaktionen werden anhand der Leistungsbilanz und der Preislage bestimmt.pair_a
und kaufenpair_b
operations.pair_a
und verkaufenpair_b
die Operationen durchgeführt werden.avg
die neuesten Preisquoten widerspiegeln.res_list
.res_list
auf Datensatzres
zur weiteren Analyse und Darstellung.pair_a = 'IOTA'
pair_b = "ZIL"
e = Exchange([pair_a,pair_b], fee=0.0002, initial_balance=10000) #Exchange definition is placed in the comments section
res_list = []
index_list = []
avg = df_close[pair_a][0] / df_close[pair_b][0]
value = 1000
for idx, row in df_close.iterrows():
diff = (row[pair_a] / row[pair_b] - avg)/avg
aim_value = -value * diff / 0.01
if -aim_value + e.account[pair_a]['amount']*row[pair_a] > 0.5*value:
e.Sell(pair_a,row[pair_a],(-aim_value + e.account[pair_a]['amount']*row[pair_a])/row[pair_a])
e.Buy(pair_b,row[pair_b],(-aim_value - e.account[pair_b]['amount']*row[pair_b])/row[pair_b])
if -aim_value + e.account[pair_a]['amount']*row[pair_a] < -0.5*value:
e.Buy(pair_a, row[pair_a],(aim_value - e.account[pair_a]['amount']*row[pair_a])/row[pair_a])
e.Sell(pair_b, row[pair_b],(aim_value + e.account[pair_b]['amount']*row[pair_b])/row[pair_b])
avg = 0.99*avg + 0.01*row[pair_a] / row[pair_b]
index_list.append(idx)
e.Update(row)
res_list.append([e.account['USDT']['total'],e.account['USDT']['hold'],
e.account['USDT']['fee'],e.account['USDT']['long'],e.account['USDT']['short']])
res = pd.DataFrame(data=res_list, columns=['total','hold', 'fee', 'long', 'short'],index = index_list)
res['total'].plot(grid=True);
Die aktuelle Korrelationsberechnung verwendet zukünftige Daten, so dass sie nicht sehr genau ist. Dieser Artikel teilt die Daten auch in zwei Teile, basierend auf der vorherigen Berechnung der Korrelation und dem anschließenden Backtest-Handel. Die Ergebnisse sind ein wenig anders, aber nicht schlecht. Wir überlassen es dem Benutzer zu üben und zu überprüfen.
Obwohl die Handelsstrategie des Paares theoretisch profitabel sein kann, bestehen im tatsächlichen Betrieb immer noch einige Risiken: Die Korrelation zwischen Währungen kann sich im Laufe der Zeit ändern, was dazu führt, dass die Strategie fehlschlägt; unter extremen Marktbedingungen können Preisabweichungen zunehmen, was zu größeren Verlusten führt; die geringe Liquidität bestimmter Währungen kann die Durchführung von Transaktionen erschweren oder die Kosten erhöhen; und die Gebühren, die durch häufige Transaktionen entstehen, können den Gewinn beeinträchtigen.
Um Risiken zu reduzieren und die Stabilität der Strategien zu verbessern, können folgende Verbesserungsmaßnahmen in Betracht gezogen werden: Regelmäßige Neuberechnung der Korrelation zwischen Währungen und rechtzeitige Anpassung der Handelspare; Festlegung von Stop-Loss- und Take-Profit-Punkten, um den maximalen Verlust eines einzelnen Geschäfts zu kontrollieren; Handel mit mehreren Währungspaaren gleichzeitig zur Diversifizierung der Risiken.
Die digitale Währungspaar-Handelsstrategie erzielt Gewinne, indem sie die Korrelation der Währungspreise nutzt und Arbitrageoperationen durchführt, wenn die Preise abweichen. Diese Strategie hat eine hohe theoretische Machbarkeit. Ein einfacher Live-Handelsstrategie-Quellcode basierend auf dieser Strategie wird später veröffentlicht. Wenn Sie weitere Fragen haben oder eine weitere Diskussion benötigen, können Sie sich gerne melden.