
Recientemente vi el diario cuantitativo de Buu que mencionaba que se pueden usar monedas correlacionadas negativamente para seleccionar monedas y abrir posiciones para obtener ganancias basadas en avances en las diferencias de precios. Las monedas digitales están básicamente correlacionadas positivamente, y solo unas pocas monedas están correlacionadas negativamente. A menudo tienen condiciones de mercado especiales, como las condiciones de mercado independientes de la moneda MEME hace algún tiempo, que no seguían en absoluto la tendencia del mercado. Filtrar Estas monedas se compran y venden en largo después del avance, este método puede generar ganancias bajo ciertas condiciones del mercado. Sin embargo, el método más común en el campo del trading cuantitativo es utilizar la correlación positiva para el trading de pares. En este artículo se presentará brevemente esta estrategia.
El trading de pares de criptomonedas es una estrategia comercial basada en el arbitraje estadístico, que busca obtener ganancias de las desviaciones de precios comprando y vendiendo simultáneamente dos contratos perpetuos de criptomonedas altamente correlacionados. Este artículo presentará los principios de la estrategia, el mecanismo de ganancias, el método de selección de divisas, los riesgos potenciales y las formas de mejorarlo, y proporcionará algunos ejemplos prácticos de código Python.
Las estrategias de trading de pares se basan en la correlación histórica entre los precios de dos criptomonedas. Cuando dos monedas están fuertemente correlacionadas, sus precios se mueven aproximadamente en sincronía. Si en un momento determinado la relación de precios entre ambos se desvía significativamente, se puede considerar que se trata de una anomalía temporal y los precios tenderán a volver a niveles normales. El mercado de divisas digitales está altamente interconectado. Cuando una moneda digital importante (como Bitcoin) experimenta fluctuaciones significativas, generalmente desencadena una reacción en cadena entre otras monedas digitales. Algunas monedas pueden tener una correlación positiva muy obvia, y dicha correlación puede mantenerse porque pertenecen a las mismas instituciones de inversión, a los mismos creadores de mercado y al mismo camino. Algunas monedas están correlacionadas negativamente, pero hay menos monedas correlacionadas negativamente y debido a que todas se ven afectadas por la tendencia general del mercado, a menudo muestran tendencias de mercado consistentes.
Supongamos que la moneda A y la moneda B tienen una alta correlación de precios. En un momento determinado, el valor medio de la relación precio A/B es 1. Si en un momento determinado, la relación de precios A/B se desvía del aumento en más de 0,001, es decir, más de 1,001, entonces puede operar de las siguientes maneras: abrir una posición larga en B y abrir una posición corta en A. . Por el contrario, cuando la relación precio A/B es inferior a 0,999: abra una posición larga en A y una posición corta en B.
La clave de la rentabilidad reside en las ganancias por diferencia de precios cuando los precios se desvían y vuelven a la normalidad. Dado que las desviaciones de precios suelen ser de corta duración, los operadores pueden cerrar sus posiciones cuando los precios vuelven a la media y obtener ganancias de la diferencia.
Estos códigos se pueden usar directamente, pero es mejor descargar Anancoda y depurarlo en un Jupyer Notebook. Incluye directamente paquetes para el análisis de datos de uso común.
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) # 获取所有的正在交易的交易对
La función principal de la función GetKlines es obtener los datos históricos de la línea K del contrato perpetuo del par comercial especificado del intercambio de Binance y almacenar estos datos en un Pandas DataFrame. Los datos de K-line incluyen precio de apertura, precio más alto, precio más bajo, precio de cierre, volumen de operaciones y otra información. Esta vez utilizamos principalmente datos de precios de cierre.
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
La cantidad de datos es relativamente grande. Para descargar más rápido, solo se obtuvieron los datos horarios de la línea K de los últimos tres meses. df_close contiene datos de precios de cierre para todas las monedas
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')
Define un objeto Exchange para la siguiente prueba retrospectiva
class Exchange:
def __init__(self, trade_symbols, fee=0.0002, initial_balance=10000):
self.initial_balance = initial_balance #初始的资产
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 #扣除手续费
self.account['USDT']['fee'] += price*amount*self.fee
self.account[symbol]['fee'] += price*amount*self.fee
if cover_amount > 0: #先平仓
self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount #利润
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): #对资产进行更新
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)
El cálculo de correlación es un método estadístico que se utiliza para medir la relación lineal entre dos variables. El método de cálculo de correlación más comúnmente utilizado es el coeficiente de correlación de Pearson. A continuación se presentan los principios, fórmulas y métodos de implementación para el cálculo de correlación. El coeficiente de correlación de Pearson se utiliza para medir la relación lineal entre dos variables, y su rango de valores está entre -1 y 1:
El coeficiente de correlación de Pearson determina la correlación entre dos variables calculando su covarianza y desviación estándar. La fórmula es la siguiente:
[ \rho_{X,Y} = \frac{\text{cov}(X,Y)}{\sigma_X \sigma_Y} ]
en:
Por supuesto, no es necesario preocuparse demasiado por cómo se calcula. Puede calcular la correlación de todas las monedas utilizando solo una línea de código Python. La figura muestra un mapa de correlación. El rojo representa una correlación positiva, el azul una correlación negativa y cuanto más oscuro es el color, más fuerte es la correlación. Se puede ver que grandes áreas son de color rojo oscuro, por lo que la correlación positiva de la moneda digital es muy fuerte.

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);
En función de la correlación, se seleccionan los 20 pares de divisas más relevantes. Los resultados son los siguientes: Sus correlaciones son muy fuertes, todas por encima de 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
El código correspondiente es el siguiente:
corr_pairs = corr.unstack()
# 移除自身相关性(即对角线上的值)
corr_pairs = corr_pairs[corr_pairs != 1]
sorted_corr_pairs = corr_pairs.sort_values(kind="quicksort")
# 提取最相关和最不相关的前20个币种对
most_correlated = sorted_corr_pairs.tail(40)[::-2]
print("最相关的前20个币种对:")
print(most_correlated)
El código de backtest específico es el siguiente. La estrategia de demostración se centra en observar la relación de precios de dos criptomonedas (IOTA y ZIL) y operar en función de los cambios en esta relación. Los pasos específicos son los siguientes:
inicialización:
e, el saldo inicial es de $10,000 y la tarifa de transacción es del 0,02%.avg。value = 1000。Procesar iterativamente los datos de precios:
df_close。diff。aim_value, por cada desviación de 0,01, intercambie un valor. Y decidir sobre operaciones de compra y venta en función de las posiciones de cuenta corriente y las condiciones de precios.pair_a y comprarpair_b funcionar.pair_a y venderpair_b funcionar.Media ajustada:
avg, con el fin de reflejar la última relación de precios.Actualización de Cuentas y Registros:
res_list。Salida de resultados:
res_list Convertir a marco de datosres, para su posterior análisis y presentación.pair_a = 'IOTA'
pair_b = "ZIL"
e = Exchange([pair_a,pair_b], fee=0.0002, initial_balance=10000) #Exchange定义放在评论区
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);
Se realizaron pruebas retrospectivas de un total de 4 grupos de monedas y los resultados fueron relativamente ideales. Los cálculos de correlación actuales utilizan datos futuros, por lo que no son muy precisos. Este artículo también divide los datos en dos partes, basadas en la correlación calculada en el frente y las transacciones probadas retrospectivamente en el reverso. Los resultados fueron un poco desiguales, pero aún así bastante buenos. Corresponde al usuario ejercer la verificación.

Aunque la estrategia de trading de pares puede ser rentable en teoría, aún existen algunos riesgos en la operación real: la correlación entre las divisas puede cambiar con el tiempo, provocando que la estrategia falle; en condiciones extremas del mercado, las desviaciones de precios pueden aumentar, lo que resulta en grandes pérdidas; La baja liquidez de ciertas monedas puede dificultar la ejecución de transacciones o aumentar los costos; las tarifas generadas por transacciones frecuentes pueden erosionar las ganancias.
Para reducir el riesgo y mejorar la estabilidad de la estrategia, se pueden considerar las siguientes medidas de mejora: recalcular periódicamente la correlación entre las divisas y ajustar los pares comerciales a tiempo; establecer puntos de stop loss y take profit para controlar la pérdida máxima de una sola transacción; Opere con múltiples pares de monedas para diversificar el riesgo.
La estrategia de negociación de pares de divisas digitales logra ganancias aprovechando la correlación entre los precios de las divisas y realizando operaciones de arbitraje cuando los precios se desvían. Esta estrategia tiene una alta viabilidad teórica. Más adelante se publicará un código fuente de estrategia en tiempo real simple basado en esta estrategia. Si tiene más preguntas o necesita más discusión, no dude en comunicarse.