리소스 로딩... 로딩...

디지털 통화 쌍 거래 전략에 대한 자세한 설명

저자:FMZ~리디아, 창작: 2024-07-08 11:41:23, 업데이트: 2024-07-12 15:54:35

img

소개

최근에, 나는 BuOu의 양적 일기를 보았는데, 당신은 부정적으로 상관관계를 가진 통화를 사용하여 통화를 선택하고 가격 차이 돌파구를 기반으로 수익을 창출하기 위해 포지션을 열 수 있다고 언급했습니다. 디지털 통화는 기본적으로 긍정적으로 상관관계를 가지고 있으며, MEME 동전의 독립적인 시장 조건과 같은 시장 추세와 완전히 다른 특별한 시장 조건과 종종 부정적인 상관관계를 가진 몇 가지 통화가 있습니다. 이러한 통화는 선택되어 돌파구 이후 오랫동안 지속될 수 있습니다. 이 방법은 특정 시장 조건 하에서 수익을 창출 할 수 있습니다. 그러나 양적 거래 분야에서 가장 일반적인 방법은 쌍 거래에 긍정적 상관관계를 사용하는 것입니다. 이 기사는 이 전략을 간략하게 소개합니다.

디지털 통화 쌍 거래는 통계적 중재에 기반을 둔 거래 전략으로, 가격 오차로부터 이익을 얻기 위해 동시에 두 개의 고도로 상관 관계를 가진 암호화폐를 구매하고 판매합니다. 이 문서에서는 이 전략의 원칙, 수익 메커니즘, 통화 선택 방법, 잠재적 위험 및 개선 방법 및 몇 가지 실용적인 파이썬 코드 예를 소개합니다.

전략 원칙

쌍 거래 전략은 두 개의 디지털 화폐의 가격 사이의 역사적 상관관계에 의존합니다. 두 개의 화폐의 가격이 강한 상관관계를 보이는 경우, 가격 추세는 일반적으로 동기화됩니다. 두 가지 사이의 가격 비율이 특정 순간에 크게 벗어나면 일시적인 이상으로 간주 될 수 있으며 가격이 정상 수준으로 돌아갈 경향이 있습니다. 디지털 화폐 시장은 매우 상호 연결되어 있습니다. 주요 디지털 화폐 (비트코인 등) 이 크게 변동하면 일반적으로 다른 디지털 화폐에 조정된 반응을 유발합니다. 일부 화폐는 동일한 투자 기관, 동일한 시장 제작자 및 동일한 트랙으로 인해 지속 될 수있는 매우 명백한 긍정적 인 상관관계를 가질 수 있습니다. 일부 화폐는 부정적인 상관관계를 가지고 있지만 부정적인 상관관계가있는 화폐가 적으며 시장 추세에 영향을 받으므로 모두 일관된 시장 추세를 가질 수 있습니다.

화폐 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

K-line 함수를 다운로드

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은 두 변수가 항상 동기화되어 변화하는 완벽한 양성 상관 관계를 나타냅니다. 한 변수가 증가하면 다른 변수도 비례적으로 증가합니다. 1에 가까워질수록 상관 관계가 강합니다.
  • -1은 두 변수가 항상 반대 방향으로 변화하는 완벽한 부정적 상관관계를 나타냅니다. -1에 가까워질수록 부정적 상관관계가 강합니다.
  • 0은 선형 상관관계가 없다는 뜻입니다. 두 변수 사이에 직선적인 관계가 없다는 뜻입니다.

피어슨 상관 계수는 두 변수 사이의 상관 관계를 계산하여 그들의 동변성 및 표준편차를 결정합니다. 공식은 다음과 같습니다.

img

그 중:

  • img변수 X와 Y 사이의 피어슨 상관 계수입니다.
  • imgX와 Y의 동변수입니다.
  • img그리고img각각 X와 Y의 표준편차입니다.

물론 계산 방법에 대해 너무 걱정할 필요가 없습니다. 모든 화폐의 상관관계를 계산하기 위해 파이썬에서 1 줄의 코드를 사용할 수 있습니다. 그림은 상관관계 열 지도를 보여줍니다. 빨간색은 긍정적 인 상관관계를 나타냅니다. 파란색은 부정적인 상관관계를 나타냅니다. 색이 어둡을수록 상관관계가 강합니다. 대부분의 영역이 어두운 빨간색이라는 것을 볼 수 있습니다. 따라서 디지털 화폐의 긍정적 인 상관관계는 매우 강합니다.

img

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) 의 가격 비율을 관찰하고이 비율의 변화에 따라 거래합니다. 구체적인 단계는 다음과 같습니다:

  1. 초기화:
  • 거래 쌍을 정의합니다 (pair_a = IOTA, pair_b = ZIL).
  • 교환 객체를 생성합니다e초기 잔액은 1만 달러이고 거래 수수료는 0.02%입니다
  • 초기 평균 가격 비율을 계산avg.
  • 초기 트랜잭션 값을 설정합니다value = 1000.
  1. 가격 데이터 반복:
  • 각 시점의 가격 데이터를 가로질러df_close.
  • 현재 가격 비율의 평균에서 오차를 계산합니다.diff.
  • 목표 거래 값은 오차를 기준으로 계산됩니다.aim_value매출과 매수 거래는 현금 계좌 위치와 가격 상황에 따라 결정됩니다.
  • 오차가 너무 크면, 실행 판매pair_a그리고 구매pair_b operations.
  • 만약 그 오차가 너무 작다면,pair_a그리고 팔아pair_b작업이 수행됩니다.
  1. 평균값을 조정합니다.
  • 평균 가격 비율을 업데이트합니다.avg가장 최근의 가격 비율을 반영하기 위해서입니다.
  1. 계정 및 기록 업데이트:
  • 교환 계좌의 위치 및 잔액 정보를 업데이트합니다.
  • 각 단계의 계좌 상태 (총자산, 보유자산, 거래 수수료, 긴 포지션 및 짧은 포지션) 를res_list.
  1. 결과 출력:
  • 변환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 개의 그룹 화폐가 백테스트되었으며 결과는 이상적이었다. 현재 상관 계산은 미래 데이터를 사용하므로 매우 정확하지 않습니다. 이 기사는 또한 이전 상관 계산과 후속 백테스트 거래를 기반으로 데이터를 두 부분으로 나눈다. 결과는 약간 다르지만 나쁘지 않습니다. 우리는 사용자가 연습하고 확인하도록 남깁니다.

img

잠재적 인 위험 과 개선 방법

쌍 거래 전략은 이론적으로 수익성이 높을 수 있지만 실제 운영에는 여전히 몇 가지 위험이 있습니다. 통화 간의 상관관계가 시간이 지남에 따라 변화하여 전략이 실패 할 수 있습니다. 극단적인 시장 조건 하에서 가격 오차가 증가하여 더 큰 손실이 발생할 수 있습니다. 특정 통화의 낮은 유동성은 거래를 수행하는 것을 어렵게하거나 비용을 증가시킬 수 있으며 빈번한 거래로 발생하는 수수료는 이익을 침식시킬 수 있습니다.

위험을 줄이고 전략의 안정성을 높이기 위해 다음과 같은 개선 조치가 고려 될 수 있습니다. 통화 간의 상관관계를 정기적으로 재 계산하고 거래 쌍을 적시에 조정하십시오. 한 거래의 최대 손실을 제어하기 위해 손해를 멈추고 수익점을 설정하십시오. 위험을 다양화하기 위해 동시에 여러 통화 쌍을 거래하십시오.

결론

디지털 통화 쌍 거래 전략은 통화 가격의 상관관계를 활용하고 가격이 변할 때 중재 작업을 수행함으로써 수익을 달성합니다. 이 전략은 높은 이론적 타당성을 가지고 있습니다. 이 전략을 기반으로 한 간단한 라이브 거래 전략 소스 코드가 나중에 출시 될 것입니다. 더 많은 질문이 있거나 추가 논의가 필요한 경우 자유롭게 연락하십시오.


더 많은