Недавно я увидел в количественном дневнике BuOu
Торговля цифровыми валютными парами - это торговая стратегия, основанная на статистическом арбитраже, которая одновременно покупает и продает две высококоррелированные криптовалюты для получения прибыли от отклонений цен.
Стратегии торговли парами опираются на историческую корреляцию между ценами двух цифровых валют. Когда цены двух валют показывают сильную корреляцию, их ценовые тенденции, как правило, синхронизируются. Если соотношение цен между ними значительно отклоняется в определенный момент, это может считаться временной аномалией, и цена будет иметь тенденцию возвращаться к нормальному уровню. Рынок цифровых валют сильно взаимосвязан. Когда крупная цифровая валюта (такая как Биткойн) значительно колеблется, это обычно вызывает скоординированную реакцию в других цифровых валютах.
Предположим, что валюта А и валюта В имеют высокую ценовую корреляцию. В определенный момент среднее значение соотношения цен A/B составляет 1. Если в определенный момент соотношение цен A/B отклоняется более чем на 0,001, то есть более чем на 1,001, то вы можете торговать следующими способами: Открыть длинную позицию на B и открыть короткую позицию на A. Напротив, когда соотношение цен A/B ниже 0,999: Открыть длинную позицию на A и открыть короткую позицию на B.
Ключ к рентабельности заключается в увеличении спреда, когда цены отклоняются от среднего и возвращаются в нормальное состояние.
Эти коды можно использовать напрямую. Лучше всего скачать Anancoda и отлаживать его в ноутбуке Jupyer. Он включает пакеты для прямого анализа данных.
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
Основная функция функции GetKlines заключается в получении исторических данных K-линии указанного торгового пара вечного контракта с биржи Binance и хранении данных в Pandas DataFrame.
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
Объем данных относительно велик. Для быстрой загрузки получаются только почасовые данные K-линии за последние три месяца. df_close содержит данные о цене закрытия всех валют.
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')
Мы определяем объект обмена для следующего обратного теста.
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)
Корреляционный расчет - это метод в статистике, используемый для измерения линейных отношений между двумя переменными. Наиболее часто используемый метод расчета корреляции - коэффициент корреляции Пирсона. Ниже приведены принцип, формула и метод реализации расчета корреляции. Коэффициент корреляции Пирсона используется для измерения линейных отношений между двумя переменными, и его диапазон значений находится в диапазоне от -1 до 1:
Коэффициент корреляции Пирсона определяет корреляцию между двумя переменными путем расчета их ковариантности и стандартного отклонения.
в которых:
Конечно, вам не нужно слишком беспокоиться о том, как это рассчитывается. Вы можете использовать 1 строку кода в Python для расчета корреляции всех валют. На рисунке показана тепловая карта корреляции. Красный представляет положительную корреляцию, синий - отрицательную корреляцию, и чем темнее цвет, тем сильнее корреляция. Вы можете видеть, что большая часть области темно-красная, поэтому положительная корреляция цифровых валют очень сильна.
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);
На основе корреляции выбираются 20 наиболее коррелирующих валютных пар. Результаты следующие. Их корреляции очень сильны, все выше 0,99.
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
Соответствующий код:
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)
Конкретный код бэкстеста выглядит следующим образом. Демонстрационная стратегия в основном наблюдает ценовое соотношение двух криптовалют (IOTA и ZIL) и торгует в соответствии с изменениями в этом соотношении. Конкретные шаги следующие:
e
с первоначальным балансом $10,000 и комиссией за транзакцию в размере 0.02%.avg
.value = 1000
.df_close
.diff
.aim_value
Операции по покупке и продаже определяются на основе текущей позиции счета и ценовой ситуации.pair_a
и купитьpair_b
operations.pair_a
и продатьpair_b
операции выполняются.avg
чтобы отразить последние коэффициенты цен.res_list
.res_list
к системе данныхres
для дальнейшего анализа и представления.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);
В общей сложности 4 группы валют были проверены, и результаты были идеальными. Текущий расчет корреляции использует будущие данные, поэтому он не очень точен. Эта статья также делит данные на две части, основываясь на предыдущем расчете корреляции и последующей торговой обратной проверке. Результаты немного отличаются, но неплохи. Мы оставляем пользователю практиковать и проверять.
Хотя стратегия торговли парой может быть прибыльной в теории, все еще существуют некоторые риски в фактической работе: корреляция между валютами может меняться с течением времени, в результате чего стратегия может потерпеть неудачу; при экстремальных рыночных условиях отклонения цен могут увеличиться, что приводит к большим потерям; низкая ликвидность некоторых валют может затруднить выполнение сделок или увеличить расходы; а сборы, генерируемые частыми операциями, могут снизить прибыль.
Для снижения рисков и повышения стабильности стратегий можно рассмотреть следующие меры улучшения: регулярно пересчитывать корреляцию между валютами и своевременно корректировать торговые пары; устанавливать точки остановки потери и получения прибыли для контроля максимальной потери одной сделки; торговать несколькими валютными парами одновременно для диверсификации рисков.
Стратегия торговли цифровыми валютными парами достигает прибыли, используя взаимосвязь цен на валюту и выполняя арбитражные операции при отклонении цен. Эта стратегия имеет высокую теоретическую осуществимость. Простой исходный код стратегии торговли на основе этой стратегии будет выпущен позже. Если у вас есть дополнительные вопросы или необходимо дальнейшее обсуждение, пожалуйста, не стесняйтесь общаться.