最近,BUEの定量化日記では,マイナス関連通貨を利用して通貨を取引し,価格差突破によって取引を開始し,利益を得ることが言及されている. デジタル通貨は基本的にはマイナス関連であり,マイナス関連は少数通貨で,しばしば特殊な市場がある. 例えば,以前のMEMEコインの独立市場は,大盘の動向を全く追わない. これらの通貨をフィルタリングし,突破後に多く行う. この方法は特定の状況下で利益を得ることができる. しかし,定量化取引の分野では,最も一般的な関連利用は正行性配列取引であり,この戦略を簡潔に説明します.
デジタル通貨配列取引は,価格偏差から利益を得るために,両者との高度な関連性のあるデジタル通貨の永続契約を同時に買ったり売ったりすることで,統計的利潤に基づいた取引戦略である.この記事では,この戦略の原理,収益メカニズム,コイン種をシフトする方法,潜在的なリスク,および改善方法について詳細に説明し,いくつかの実用的なPythonコードの例を提供します.
配列取引戦略は,2つのデジタル通貨の価格の間の歴史的関連性に依存する. 二つの通貨の価格が強い関連性を示すとき,その価格動向はほぼ同期する. もし,ある時点で両者の価格比率が著しく偏った場合,これは一時的な異常とみなされ,価格が正常水準に戻る傾向がある. デジタル通貨市場は高度な連動性があり,主要なデジタル通貨であるビットコインが大きく波動するときに,他のデジタル通貨の連動反応を誘発することが多い. いくつかの通貨は,同じ投資機関,同じ取引者,同じ競争の理由によって非常に顕著な正則的な関連性があり,持続可能である.
通貨Aと通貨Bが高い価格相関性を持っていると仮定する.ある時点でA/Bの価格比率が平均値である.ある時点でA/Bの価格比率が0.001以上,すなわち1.001以上上昇した場合,以下のように取引することができます.
利潤の鍵は,価格偏差が正常に戻ると差利回しである.価格偏差は通常短期間であるため,トレーダーは価格が平均値に戻ると平衡し,利益を得て差利を稼ぐことができる.
これらのコードは直接利用可能で,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の主な機能は,ビットコイン取引所から指定された取引の永続契約の歴史的な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線時間データのみが入手されている.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)
関連性計算は,統計学における2つの変数の間の線形関係を測定する方法である.最もよく使われる関連性計算方法は,ピアソンの関連系数である.以下は関連性計算の原理,公式,実装方法である.ピアソンの関連系数が,2つの変数の間の線形関係を測定するために使用され,値の範囲は-1から1の間である:
ピルソン相関系数は,両変数の共関差と標準差を計算して相関関係を決定する.式は以下のとおりである.
グラフの表は,x,yの表で,x,yの表は,x,yの表で
その中には:
もちろん,計算方法に関心なく,Pythonの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()
# 移除自身相关性(即对角线上的值)
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)
具体的には,コードを繰り返す. デモンストレーション戦略の主な観察は2つの暗号通貨 (IOTAとZIL) の価格比率であり,この比率の変化に基づいて取引を行う. 具体的ステップは以下の通りである.
初期化:
e
初期残高は1万ドルで,取引手数料は0.02%です.avg
。value = 1000
。代行処理価格データ:
df_close
。diff
。aim_value
0.01 の偏差ごとに,1 値で取引します. そして,現在の口座保有と価格状況に基づいて買取・売却を決定します.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つの通貨群を合計して再テストした結果,結果は比較的理想的です. 現在の関連性計算は将来のデータを使用しているため,非常に正確ではありません. 本文はまた,データを2つの部分に分割し,前回の計算関連性に基づいて,後の再テスト取引の結果は,少し悪いが良好です. ユーザの自己の練習検証を残します.
配列取引戦略は理論上有利であるものの,実用操作ではいくつかのリスクがあります:通貨間の関連性が時間の経過とともに変化し,戦略が失敗する可能性があります. 極端な市場条件下では,価格偏差が激化し,大きな損失を引き起こす可能性があります. 特定の通貨の流動性が低く,取引が実行に困難またはコストが増加する可能性があります. 頻繁な取引による手続費が利益を蝕む可能性があります.
リスクを軽減し,戦略の安定性を高めるために,以下の改善策を検討することができる: 通貨間の関連性を定期的に再計算し,取引対を適時に調整する; ストップ損失とストップピークを設定し,単一の取引の最大損失を制御する; 同時に複数の通貨対を取引し,リスクを分散する.
デジタル通貨配列取引戦略は,通貨価格の関連性を利用し,価格偏差時に利息操作を行い,利益を得ることを目的とする.この戦略は理論上高い可行性を有する.その後,この戦略に基づいたシンプルなリアルタイムの戦略ソースコードがリリースされる.より多くの質問やさらなる議論が必要な場合は,いつでも連絡してください.
77924998オープンソースは研究に値するものです.
豆888張総裁は - ハハハ!