記事でバックテストエンジンを公開しました
まず,歴史的なKラインとは何か? 1つのKラインデータには4つの価格が含まれます.最高価格,開場価格,最低価格,閉場価格,開始時間,終了時間,間隔取引量.ほとんどの定量プラットフォームとフレームワークはKラインに基づいてバックテストされ,FMZ量子プラットフォームはTickレベルバックテストも提供しています.Kラインバックテストのスピードは非常に高速で,ほとんどの場合問題ありませんが,特にマルチバラエティ戦略とバックテストの高周波戦略が非常に深刻な欠陥があり,正しい結論を出すことはほとんどできません.
まず第一に,時間の問題です.K線データの最高値と最低値の時間は与えられていないので,考慮する必要はありませんが,最も重要な開閉価格は開閉価格ではありません.取引品種が人気がない場合でも,それらはしばしば10秒以上取引されません.複数の多様性の戦略をバックテストするとき,開閉価格が同じであることを通常デフォルトで確認します.これはまた,閉閉価格バックテストの基礎でもあります.
2つの品種の仲介をバックテストするために分数線を使用することを想像してください. その間の差は通常10元です. 10:01で,契約Aの閉値が100元,契約Bの閉値が112元,差が12元であることが判明します. したがって,戦略はヘッジを開始します. ある時点で,差は返り,戦略は返金利益の2元を得ます.
しかし,実際の状況は10時45分に起こる可能性があります.A契約は100元取引を生み出し,その後取引はありませんでした.B契約は10時58分に112元取引を生み出しました.10時01分には,両価格も存在しませんでした.この時点で開口価格は何でしたか?そして,ヘッジはどのくらいの違いを得ることができますか?我々は知らない.ある可能性のある状況は,10時58分には,A契約の1つを購入し,1つ売却するトレンドが101.9-102.1であり,2元のスプレッドはありません.これは戦略最適化を大きく誤導します.
2つ目はマッチメイキングです.実際のマッチメイキングは価格と時間です.もし購入者が販売価格を超えると,通常は販売価格で取引を完了します.そうでなければ,注文簿に入力して待ちます.Kラインデータには,販売価格や購入価格がないことは明らかです.詳細レベルでのマッチングをシミュレートすることはできません.
最後に,戦略そのものの取引が市場に与える影響です.もし小規模なファンドバックテストであれば,影響は小さくなります.しかし,取引量が大部分を占める場合,それは市場に影響を与えるでしょう.取引が直ちに完了すると価格スリップポイントが大きくなるだけでなく,バックテストで購入オーダーが完了すると,実際に購入したい他のオリジナルトレーダーの取引を先制します.これは市場に蝶効果の影響を与えるでしょう.しかし,この影響は定量化することはできません.そして,高周波取引は小さな資金しか収容できないと経験によってのみ言える.
FMZは,実際のボットレベルのバックテストを提供し,実際の20レベルの歴史的深さ,リアルタイム秒のティック,トランザクションごとにトランザクション,その他のデータを取得することができます.https://www.fmz.com/m/database) この種のバックテスト測定には大量のデータと遅い速度があり,わずか2日間しか使用できない.比較的頻度が高い,または厳格な時間判断を必要とする戦略では,実際のボットバックテストが必要である.FMZが収集した取引ペアと時間はそれほど長くないが,7000億個以上の歴史的データがある.現在のマッチングメカニズムは,購入オーダーが販売オーダーよりも大きい場合,量を見ることなくすぐに完全にマッチされ,購入オーダーが販売オーダーよりも小さい場合,マッチングキューに入ります.このバックテストメカニズムはKラインバックテストの最初の2つの問題を解決しますが,まだ最後の問題を解決することはできません.データ量が大きすぎるため,バックテストの速度と時間は制限されています.
Kラインに関する情報はあまりにも少ないし,深さは偽りでもある可能性があります.しかし,ある種のデータは,最も実際の取引履歴を反映する市場の実際の取引意図です.つまり,取引ごとに取引です.この論文では,注文フローに基づいた高周波バックテストシステムを提案します.これは,実際のボットレベルでのバックテストのデータの量を大幅に削減し,ある程度,市場への取引量の影響をシミュレートします.
過去5日間の取引ごとに取引をダウンロードしました バイナンスXTZ永久契約 (ダウンロードアドレス:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv) の213000個のデータがあります.データの構成を見てみましょう.
[['XTZ', 1590981301905, 2.905, 0.4, 'False\n'],
['XTZ', 1590981303044, 2.903, 3.6, 'True\n'],
['XTZ', 1590981303309, 2.903, 3.7, 'True\n'],
['XTZ', 1590981303738, 2.903, 238.1, 'True\n'],
['XTZ', 1590981303892, 2.904, 0.1, 'False\n'],
['XTZ', 1590981305250, 2.904, 0.1, 'False\n'],
['XTZ', 1590981305643, 2.903, 197.3, 'True\n'],
データとは2次元リストで,トランザクション時間によって並べられている.具体的な意味は:種名,トランザクション価格,トランザクションタイムスタンプ,トランザクション量,および販売オーダーが積極的に実行されているかどうかである. 売買と売買の両方が存在する.各トランザクションには買い手と売り手が含まれます.買い手はマーケットメーカーの場合,売り手はアクティブトランザクションテイカーである場合,最後のデータはTrueになります.
まず,取引の方向性により,市場での買取と売却を正確に推測することができます. 活発な販売オーダーである場合,この時点で買取価格が取引価格です. 活発な購入オーダーである場合,販売価格が取引価格です. 新しい取引がある場合は,新しい開設ポジションを更新します. 更新されていない場合は,最後の結果が保持されます. 上記のデータの最後の瞬間を起動することは簡単です. 購入価格は2.903,販売価格は2.904です.
オーダーフローに従って,以下のようにマッチすることができます:購入注文を例として,価格が価格であり,注文量が金額である.この時点で,開設ポジションの1つを購入し,1つ売却すると,それぞれオファーとオファーされます.価格がオファーよりも低く,オファーよりも高くなった場合,それは最初にメーカーとして判断され,マッチメイキングに優先度を与えることができます.その後,オーダーの生期間に価格よりも低いまたは価格に等しい取引価格を持つすべての取引が,このオーダーにマッチされます (価格がオファーよりも低いまたは価格に等しい場合,取引に優先度を与えることはできません,価格よりも低い取引価格を持つ注文は,このオーダーにマッチされます).マッチメイキング価格は価格であり,取引量は取引ごとに取引の頻度であり,取引が完全に終了またはキャンセルされるまでです.価格の差が高くなった場合,それはテイカーとして判断されます.この取引の為,すべての価格と取引の間の価格のマッチングは優先順位となります.この取引の為,取引の価格と引き換えのコストは高いです.この取引の為,すべての取引の価格と引き換えの為,取引の価格が
このマッチメイキングの問題は簡単に見えてきます.オーダーがテイカーである場合,実際の状況は,新しいオーダーがマッチするのを待つのではなく,すぐに取引を行うことができるということです.まずは,市場にリストされているオーダーの数を考慮しませんでした.データがあったとしても,取引の直接的な判断は深さを変化させ,市場に影響を与えます.新しいオーダーに基づくマッチメイキングは,歴史上の実際のオーダーに置き換えることに相当します.それはいかなる場合でも市場の取引量制限を超えないし,最終利益は市場が生み出す最大利益を超えないでしょう.マッチメイキングメカニズムの一部は,注文の取引量にも影響し,それによって戦略のリターンに影響を与え,戦略を定量的に反映します.資金が倍増した場合のリターンの額が倍増する伝統的なバックテストはありません.
注文の購入価格が1つの購入価格に等しい場合,注文が1つの購入価格にマッチする確率は依然としてある.注文の優先度と取引確率は考慮する必要があります.これはより複雑で,ここでは考慮されません.
交換オブジェクトは,基本的には変更されない初期導入を参照することができます. メーカーとテイカー・コミッションの違いのみが追加され,バックテストの速度が最適化されます. メイッチメイキングコードは主に以下に紹介されています.
symbol = 'XTZ'
loop_time = 0
intervel = 1000 #The sleep time of the strategy is 1000ms
init_price = data[0][2] #Initial price
e = Exchange([symbol],initial_balance=1000000,maker_fee=maker_fee,taker_fee=taker_fee,log='') #Initialize the exchange
depth = {'ask':data[0][2], 'bid':data[0][2]} #depth
order = {'buy':{'price':0,'amount':0,'maker':False,'priority':False,'id':0},
'sell':{'price':0,'amount':0,'maker':False,'priority':False,'id':0}} #Order
for tick in data:
price = int(tick[2]/tick_sizes[symbol])*tick_sizes[symbol] #Transaction price
trade_amount = tick[3] #Number of transactions
time_stamp = tick[1] #Transaction timestamp
if tick[4] == 'False\n':
depth['ask'] = price
else:
depth['bid'] = price
if depth['bid'] < order['buy']['price']:
order['buy']['priority'] = True
if depth['ask'] > order['sell']['price']:
order['sell']['priority'] = True
if price > order['buy']['price']:
order['buy']['maker'] = True
if price < order['sell']['price']:
order['sell']['maker'] = True
#Order network delay can also be used as one of the matching conditions, which is not considered here
cond1 = order['buy']['priority'] and order['buy']['price'] >= price and order['buy']['amount'] > 0
cond2 = not order['buy']['priority'] and order['buy']['price'] > price and order['buy']['amount'] > 0
cond3 = order['sell']['priority'] and order['sell']['price'] <= price and order['sell']['amount'] > 0
cond4 = not order['sell']['priority'] and order['sell']['price'] < price and order['sell']['amount'] > 0
if cond1 or cond2:
buy_price = order['buy']['price'] if order['buy']['maker'] else price
e.Buy(symbol, buy_price, min(order['buy']['amount'],trade_amount), order['buy']['id'], order['buy']['maker'])
order['buy']['amount'] -= min(order['buy']['amount'],trade_amount)
e.Update(time_stamp,[symbol],{symbol:price})
if cond3 or cond4:
sell_price = order['sell']['price'] if order['sell']['maker'] else price
e.Sell(symbol, sell_price, min(order['sell']['amount'],trade_amount), order['sell']['id'], order['sell']['maker'])
order['sell']['amount'] -= min(order['sell']['amount'],trade_amount)
e.Update(time_stamp,[symbol],{symbol:price})
if time_stamp - loop_time > intervel:
order = get_order(e,depth,order) #Trading logic, not given here
loop_time += int((time_stamp - loop_time)/intervel)*intervel
いくつかの詳細は注目に値します:
-1. 新しい取引があるとき,最初に注文をマッチし,次に最新の価格に従って注文をします.
-2.各注文には2つの属性があります. メーカー
最後に,実際のバックテスト段階に達します. ここで,最もクラシックなグリッド戦略をバックテストして,それが期待された効果を達成したかどうかを確認します. 戦略原則は,価格が1%上昇するたびに,我々は特定の値のショートポジションオーダー (そうでなければ,我々はロングポジションオーダーを保持する) を保持し,我々は購入オーダーと販売オーダーを計算し,それらを事前に待機します. コードはリリースされません. すべてのコードを関数に封筒化します.Grid ('XTZ ', 100,0.31000, maker_fee=-0.00002, taker_fee=0.0003)
パラメータは:取引ペア,価格偏差1%の保有価値,注文密度0.3%,睡眠間隔 ms,待機オーダー・コミッション,テイカー・コミッションです.
XTZ市場は 5日間にショックを受けています これはグリッド戦略に非常に適しています
まず,各ポジションのリターンへの影響をバックテストします.従来のバックテストメカニズムで測定されたリターンは,ポジションの増加に比例して確実に増加します.
e1 = Grid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e1.account['USDT'])
e2 = Grid('XTZ',1000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e2.account['USDT'])
e3 = Grid('XTZ',10000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e3.account['USDT'])
e4 = Grid('XTZ',100000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e4.account['USDT'])
合計4つのグループが100,1000,10000および100000の位置値でバックテストされ,バックテストの総時間は1.3秒でした.結果は以下のとおりです.
{'realised_profit': 28.470993031132966, 'margin': 0.7982662957624465, 'unrealised_profit': 0.0104554474048441, 'total': 10000028.481448, 'leverage': 0.0, 'fee': -0.3430967859046398, 'maker_fee': -0.36980249726699727, 'taker_fee': 0.026705711362357405}
{'realised_profit': 275.63148945320177, 'margin': 14.346335829979132, 'unrealised_profit': 4.4382117331794045e-14, 'total': 10000275.631489, 'leverage': 0.0, 'fee': -3.3102045933457784, 'maker_fee': -3.5800688964477048, 'taker_fee': 0.2698643031019274}
{'realised_profit': 2693.8701498889504, 'margin': 67.70120400534114, 'unrealised_profit': 0.5735269329348516, 'total': 10002694.443677, 'leverage': 0.0001, 'fee': -33.984021415250744, 'maker_fee': -34.879233866850974, 'taker_fee': 0.8952124516001403}
{'realised_profit': 22610.231198585603, 'margin': 983.3853688758861, 'unrealised_profit': -20.529965947304365, 'total': 10022589.701233, 'leverage': 0.002, 'fee': -200.87094000385412, 'maker_fee': -261.5849078470078, 'taker_fee': 60.71396784315319}
最終的に実現した利益は,それぞれポジション価値の28.4%,27.5%,26.9%および22.6%であることが見られる.これは実際の状況にも一致している.ポジション価値が大きいほど,オーダーの価値が高くなり,部分取引が起こる可能性が高い.最終的に実現された収益はオーダーの金額に比べて小さい.下図は相対的収益をそれぞれ100と10000のポジション値と比較している.
また,待機中の注文密度,睡眠時間,手数料など,バックテストの返信に対する異なるパラメータの影響をバックテストすることもできます.睡眠時間を例として取り,100msに変更し,1000msの睡眠時間と比較して,返信を観察します.バックテスト結果は以下のとおりです:
{'realised_profit': 29.079440803790423, 'margin': 0.7982662957624695, 'unrealised_profit': 0.0104554474048441, 'total': 10000029.089896, 'leverage': 0.0, 'fee': -0.3703702128662524, 'maker_fee': -0.37938946377435134, 'taker_fee': 0.009019250908098965}
利益は少し増加した.これは,戦略に待機している注文の1つのグループのみであり,変更する時間がないため,変動する価格を得ることができない注文があるためである.睡眠時間が短縮されることでこの問題が改善される.これはグリッド戦略における待機しているマルチグループ注文の重要性を示している.
この論文は,オーダーフローを基礎とした新しいバックテストシステムを革新的に提案しており,部分的にオーダー待機,オーダー受領,部分取引,遅延などのマッチング状況をシミュレートすることができ,部分的に戦略ファンド量の収益への影響を反映し,高周波戦略とヘッジ戦略にとって重要な基準値を持っています.高精度バックテストは戦略パラメータの最適化方向を示します.また,長期間のリアルボットテストによって検証されています.そしてバックテストに必要なデータの量はよく制御されており,バックテストの速度も非常に速いです.