최근에, 나는 BuOu
디지털 통화 쌍 거래는 통계적 중재에 기반을 둔 거래 전략으로, 가격 오차로부터 이익을 얻기 위해 동시에 두 개의 고도로 상관 관계를 가진 암호화폐를 구매하고 판매합니다. 이 문서에서는 이 전략의 원칙, 수익 메커니즘, 통화 선택 방법, 잠재적 위험 및 개선 방법 및 몇 가지 실용적인 파이썬 코드 예를 소개합니다.
쌍 거래 전략은 두 개의 디지털 화폐의 가격 사이의 역사적 상관관계에 의존합니다. 두 개의 화폐의 가격이 강한 상관관계를 보이는 경우, 가격 추세는 일반적으로 동기화됩니다. 두 가지 사이의 가격 비율이 특정 순간에 크게 벗어나면 일시적인 이상으로 간주 될 수 있으며 가격이 정상 수준으로 돌아갈 경향이 있습니다. 디지털 화폐 시장은 매우 상호 연결되어 있습니다. 주요 디지털 화폐 (비트코인 등) 이 크게 변동하면 일반적으로 다른 디지털 화폐에 조정된 반응을 유발합니다. 일부 화폐는 동일한 투자 기관, 동일한 시장 제작자 및 동일한 트랙으로 인해 지속 될 수있는 매우 명백한 긍정적 인 상관관계를 가질 수 있습니다. 일부 화폐는 부정적인 상관관계를 가지고 있지만 부정적인 상관관계가있는 화폐가 적으며 시장 추세에 영향을 받으므로 모두 일관된 시장 추세를 가질 수 있습니다.
화폐 A와 화폐 B가 높은 가격 상관관계를 가지고 있다고 가정합니다. 특정 순간에 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-라인 데이터를 획득하고 데이터를 판다스 데이터 프레임에 저장하는 것입니다. 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 라인 데이터만 얻을 수 있다. 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 줄의 코드를 사용할 수 있습니다. 그림은 상관관계 열 지도를 보여줍니다. 빨간색은 긍정적 인 상관관계를 나타냅니다. 파란색은 부정적인 상관관계를 나타냅니다. 색이 어둡을수록 상관관계가 강합니다. 대부분의 영역이 어두운 빨간색이라는 것을 볼 수 있습니다. 따라서 디지털 화폐의 긍정적 인 상관관계는 매우 강합니다.
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
초기 잔액은 1만 달러이고 거래 수수료는 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 개의 그룹 화폐가 백테스트되었으며 결과는 이상적이었다. 현재 상관 계산은 미래 데이터를 사용하므로 매우 정확하지 않습니다. 이 기사는 또한 이전 상관 계산과 후속 백테스트 거래를 기반으로 데이터를 두 부분으로 나눈다. 결과는 약간 다르지만 나쁘지 않습니다. 우리는 사용자가 연습하고 확인하도록 남깁니다.
쌍 거래 전략은 이론적으로 수익성이 높을 수 있지만 실제 운영에는 여전히 몇 가지 위험이 있습니다. 통화 간의 상관관계가 시간이 지남에 따라 변화하여 전략이 실패 할 수 있습니다. 극단적인 시장 조건 하에서 가격 오차가 증가하여 더 큰 손실이 발생할 수 있습니다. 특정 통화의 낮은 유동성은 거래를 수행하는 것을 어렵게하거나 비용을 증가시킬 수 있으며 빈번한 거래로 발생하는 수수료는 이익을 침식시킬 수 있습니다.
위험을 줄이고 전략의 안정성을 높이기 위해 다음과 같은 개선 조치가 고려 될 수 있습니다. 통화 간의 상관관계를 정기적으로 재 계산하고 거래 쌍을 적시에 조정하십시오. 한 거래의 최대 손실을 제어하기 위해 손해를 멈추고 수익점을 설정하십시오. 위험을 다양화하기 위해 동시에 여러 통화 쌍을 거래하십시오.
디지털 통화 쌍 거래 전략은 통화 가격의 상관관계를 활용하고 가격이 변할 때 중재 작업을 수행함으로써 수익을 달성합니다. 이 전략은 높은 이론적 타당성을 가지고 있습니다. 이 전략을 기반으로 한 간단한 라이브 거래 전략 소스 코드가 나중에 출시 될 것입니다. 더 많은 질문이 있거나 추가 논의가 필요한 경우 자유롭게 연락하십시오.