
최근에 저는 Buu의 양적 일기를 보았는데, 거기에는 음의 상관관계를 갖는 통화를 사용하여 통화를 선택할 수 있고, 가격 차이 돌파에 따라 수익을 내기 위해 포지션을 오픈할 수 있다는 내용이 언급되어 있었습니다. 디지털 통화는 기본적으로 양의 상관관계가 있고, 소수의 통화만이 음의 상관관계를 가지고 있습니다. 종종 MEME 코인의 독립적인 시장 조건과 같이 시장 추세를 전혀 따르지 않는 특별한 시장 조건이 있습니다. 필터링 이러한 통화들은 돌파구가 생긴 후에도 오랫동안 가격이 유지되며, 이 방법은 특정 시장 조건 하에서 수익을 낼 수 있습니다. 그러나 양적 거래 분야에서 가장 흔한 방법은 페어 거래에 양의 상관관계를 사용하는 것입니다. 이 글에서는 이 전략을 간략하게 소개합니다.
암호화폐 페어 트레이딩은 통계적 차익거래를 기반으로 한 트레이딩 전략으로, 상관관계가 높은 두 개의 암호화폐 영구 계약을 동시에 매수 및 매도하여 가격 편차를 이용해 수익을 올리는 것을 목표로 합니다. 이 글에서는 전략의 원칙, 수익 메커니즘, 통화 선별 방법, 잠재적 위험 및 이를 개선하는 방법을 소개하고 몇 가지 실용적인 Python 코드 예제를 제공합니다.
페어 트레이딩 전략은 두 암호화폐의 가격 간의 과거 상관관계에 의존합니다. 두 통화가 강력한 상관관계를 가지고 있으면, 두 통화의 가격은 대략적으로 동기화되어 움직입니다. 특정 순간에 두 가격의 비율에 큰 차이가 생긴다면 이는 일시적인 이상 현상으로 볼 수 있으며, 가격은 정상 수준으로 돌아가는 경향이 있습니다. 디지털 통화 시장은 매우 상호 연결되어 있습니다. 주요 디지털 통화(예: 비트코인)가 상당한 변동을 겪을 때 일반적으로 다른 디지털 통화 간의 연쇄 반응을 유발합니다. 일부 통화는 매우 확실한 긍정적 상관관계를 보일 수 있으며, 같은 투자 기관, 같은 시장 조성자, 같은 트랙에 속하기 때문에 그 상관관계가 지속될 수도 있습니다. 일부 통화는 음의 상관관계를 가지고 있지만, 음의 상관관계를 가진 통화는 그 수가 적고, 모두 전체 시장 추세에 영향을 받기 때문에 일관된 시장 추세를 보이는 경우가 많습니다.
동전 A와 동전 B의 가격 상관관계가 높다고 가정해보자. 특정 시점에서 A/B 가격 비율의 평균값은 1입니다. 특정 시점에서 A/B 가격 비율이 증가에서 0.001 이상, 즉 1.001 이상 벗어나면 다음과 같은 방식으로 거래할 수 있습니다. B에 롱 포지션을 열고 A에 숏 포지션을 엽니다. . 반대로, A/B 가격 비율이 0.999보다 낮은 경우: A에 롱 포지션을, B에 숏 포지션을 엽니다.
수익성의 핵심은 가격이 변동하여 정상화될 때 발생하는 가격 차이의 이익에 있습니다. 가격 편차는 보통 단기적으로 지속되므로, 트레이더는 가격이 평균으로 회귀하면 포지션을 정리하고 그 차이에서 수익을 낼 수 있습니다.
이러한 코드는 직접 사용할 수도 있지만, Anancoda를 다운로드하여 Jupyer Notebook에서 디버깅하는 것이 가장 좋습니다. 일반적으로 사용되는 데이터 분석을 위한 패키지를 직접 포함합니다.
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) # 获取所有的正在交易的交易对
GetKlines 함수의 주요 기능은 Binance 거래소에서 지정된 거래 쌍의 무기한 계약에 대한 과거 K-라인 데이터를 얻고 이 데이터를 Pandas DataFrame에 저장하는 것입니다. K-라인 데이터에는 시가, 최고가, 최저가, 종가, 거래량 및 기타 정보가 포함됩니다. 이번에는 주로 종가 데이터를 활용했습니다.
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
데이터 양이 비교적 많습니다. 더 빠르게 다운로드하기 위해 지난 3개월 동안의 시간별 K-line 데이터만 얻었습니다. 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')
다음 백테스트에 대한 Exchange 객체를 정의합니다.
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)
상관관계 계산은 두 변수 간의 선형 관계를 측정하는 데 사용되는 통계학적 방법입니다. 가장 일반적으로 사용되는 상관관계 계산 방법은 피어슨 상관 계수입니다. 상관관계 계산의 원칙, 공식, 구현 방법은 아래와 같습니다. 피어슨 상관 계수는 두 변수 간의 선형 관계를 측정하는 데 사용되며 값 범위는 -1과 1 사이입니다.
피어슨 상관계수는 공분산과 표준편차를 계산하여 두 변수 간의 상관관계를 결정합니다. 공식은 다음과 같습니다.
[ \rho_{X,Y} = \frac{\text{cov}(X,Y)}{\sigma_X \sigma_Y} ]
안에:
물론, 계산 방법에 대해 너무 걱정할 필요는 없습니다. 단 한 줄의 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()
# 移除自身相关性(即对角线上的值)
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)
구체적인 백테스트 코드는 다음과 같습니다. 데모 전략은 두 가지 암호화폐(IOTA와 ZIL)의 가격 비율을 관찰하고 이 비율의 변화에 따라 거래하는 데 중점을 둡니다. 구체적인 단계는 다음과 같습니다.
초기화:
e초기 잔액은 10,000달러이고 거래 수수료는 0.02%입니다.avg。value = 1000。반복적으로 가격 데이터 처리:
df_close。diff。aim_value0.01의 편차마다 하나의 값을 교환합니다. 그리고 현재 계좌 포지션과 가격 상황에 따라 매수 및 매도 작업을 결정합니다.pair_a 그리고 구매하다pair_b 작동하다.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定义放在评论区
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개의 통화 그룹에 대해 백테스트를 실시하였고, 그 결과는 비교적 이상적이었습니다. 현재의 상관관계 계산은 미래의 데이터를 사용하므로 그다지 정확하지 않습니다. 이 글에서는 또한 데이터를 두 부분으로 나눕니다. 앞부분은 계산된 상관관계를 나타내고, 뒷부분은 백테스트된 거래를 나타냅니다. 결과는 약간 떨어졌지만 여전히 꽤 좋았습니다. 검증은 사용자에게 맡겨집니다.

페어 트레이딩 전략은 이론상으로는 수익성이 있을 수 있지만, 실제로 운영되는 데는 여전히 몇 가지 위험이 있습니다. 즉, 시간이 지남에 따라 통화 간 상관관계가 변하여 전략이 실패할 수 있으며, 극단적인 시장 상황에서는 가격 편차가 커져 큰 손실이 발생할 수 있습니다. 특정 통화의 유동성이 낮아 거래 실행이 어려워지거나 비용이 증가할 수 있으며, 빈번한 거래로 인해 발생하는 수수료로 인해 수익이 잠식될 수 있습니다.
위험을 줄이고 전략의 안정성을 높이기 위해 다음과 같은 개선 조치를 고려할 수 있습니다. 정기적으로 통화 간 상관관계를 다시 계산하고 거래 쌍을 적절한 시기에 조정합니다. 손절매 지점과 이익실현 지점을 설정하여 단일 거래의 최대 손실을 통제합니다. 위험을 분산하기 위해 다양한 코인 쌍을 거래하세요.
디지털 통화 쌍 거래 전략은 통화 가격 간의 상관관계를 활용하고, 가격이 변동할 때 차익거래를 수행하여 수익을 창출합니다. 이 전략은 이론적으로 실현 가능성이 높습니다. 이 전략을 기반으로 한 간단한 실시간 전략 소스 코드는 나중에 공개될 예정입니다. 더 궁금한 점이 있거나 추가 논의가 필요하면 언제든지 문의하세요.