資源の読み込みに... 荷物...

デジタル通貨ペア取引戦略の詳細な説明

作者: リン・ハーンFMZ~リディア, 作成: 2024-07-08 11:41:23, 更新: 2024-11-05 17:43:03

img

紹介

最近,私はBuOuの定量日記で,通貨を選択するために負の相関通貨を使用し,価格差突破に基づいて利益を得るためにポジションを開くことを言及しました. デジタル通貨は基本的に正の相関関係があり,わずかな通貨のみが負の相関関係があり,しばしば市場傾向とは完全に異なるMEMEコインの独立した市場状況などの特殊な市場状況にあります. これらの通貨は,突破後も長く選択され続けることができます. この方法は特定の市場状況下で利益を得ることができます. しかし,定量取引の分野で最も一般的な方法は,ペア取引のための正の相関関係を使用することです. この記事は,この戦略を簡潔に紹介します.

デジタル通貨ペア取引 (Digital currency pair trading) は,価格偏差から利益を得るため,高度に相関する2つの暗号通貨を同時に購入・販売し,統計的仲介に基づく取引戦略である.この記事では,この戦略の原則,利益メカニズム,通貨選択方法,潜在的なリスクおよび改善方法,およびいくつかの実践的なPythonコード例を紹介する.

戦略原則

ペア取引戦略は,2つのデジタル通貨の価格との間の歴史的相関に依存する. 2つの通貨の価格が強い相関を示すとき,その価格動向は一般的に同期している. 2つの間の価格比が特定の時点で大幅に偏差した場合,それは一時的な異常とみなされ,価格は通常のレベルに戻る傾向がある. デジタル通貨市場は高度に相互に関連している.主要なデジタル通貨 (Bitcoinなどの) が大幅に変動すると,通常他のデジタル通貨に調整された反応を引き起こす. いくつかの通貨は,同じ投資機関,同じマーケットメーカーの存在,同じトレックによる非常に明らかなポジティブな相関を持つ可能性があります. いくつかの通貨は否定的に相関していますが,否定的に相関する通貨が少なくなり,それらはすべて市場動向に影響を受けますので,それらはしばしば一貫した市場動向を持っています.

通貨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線関数をダウンロードする

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)

フィルター通貨との相関分析

相互関係計算は,統計学で2つの変数間の線形関係を測定するために使用される方法である.最も一般的に使用される相互関係計算方法は,ピアソン相互関係係数である.以下は,相互関係計算の原理,公式および実施方法である.ピアソン相互関係係数は,2つの変数間の線形関係を測定するために使用され,その値範囲は-1から1の間である:

  • 1 は,両変数が常に同期して変化する完全な正関関係を示します. 1 変数が増加すると,他の変数も比例して増加します. 1 に近いほど,関係が強くなります.
  • -1 は完全な負の相関を示し,2つの変数が常に反対方向に変化する. -1 に近づくほど負の相関が強くなります.
  • 0は線形的な相関がないことを意味します 2つの変数間には直線的な関係はありません

ピアソン相関係数は,両変数の共変数と標準偏差を計算することによって,両変数の相関を決定する.式は以下のとおりである.

img

その内:

  • img変数XとYのピアソン相関係数です
  • imgXとYの共変数です
  • imgそしてimgXとYの標準偏差です

もちろん,計算方法についてはあまり心配する必要はありません.すべての通貨の相関を計算するために,Pythonで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)

バックテスト 検証

具体的なバックテストコードは以下の通りである.デモ戦略は主に2つの暗号通貨 (IOTAとZIL) の価格比を観察し,この比の変化に応じて取引する.具体的なステップは以下のとおりである:

  1. 初期化:
  • 取引ペアを定義する (pair_a = IOTA, pair_b = ZIL).
  • 交換オブジェクトを作成e取引手数料は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つの通貨グループがバックテストされ,結果は理想的でした.現在の相関計算は将来のデータを使用しているため,非常に正確ではありません.この記事では,以前の相関計算と後のバックテスト取引に基づいてデータを2つの部分に分けます.結果は少し異なりますが,悪くはありません.私たちはそれを実践し検証するユーザーに任します.

img

潜在 的 な 危険 と 改善 の 方法

理論上,ペア取引戦略は利益をもたらすかもしれないが,実際の運用には依然としていくつかのリスクがある. 通貨間の相関は時間の経過とともに変化し,戦略が失敗する可能性がある. 極端な市場状況下で,価格偏差が増加し,大きな損失を引き起こす可能性がある. 特定の通貨の低流動性は取引を実行しやすくしたりコストを増やすこともあり,頻繁な取引によって生じる手数料は利益を損なう可能性がある.

リスクを軽減し,戦略の安定性を向上させるために,以下の改善措置を考慮することができる.通貨間の相関を定期的に再計算し,取引ペアを適時に調整する. 取引の最大損失を制御するためにストップ・ロスを設定し,利益を取るポイントを設定する. リスクを多様化するために,複数の通貨ペアを同時に取引する.

結論

デジタル通貨ペア取引戦略は,通貨価格の相関性を活用し,価格が逸脱したときの仲介操作を実行することで利益を達成する.この戦略は高い理論的実行可能性を有する.この戦略に基づいた簡単なライブ取引戦略ソースコードは後でリリースされる.より多くの質問またはさらなる議論が必要な場合は,自由に連絡してください.


もっと