高周波取引戦略を紹介する記事を書きました (https://www.fmz.com/digest-topic/6228) にもかかわらず,少しの注目を集めましたが,あまり深くありませんでした.それ以来2年半が経過し,市場は変わりました.私がその記事を掲載した後,私の高周波戦略は長い間安定した利益を得ることができましたが,利益は徐々に低下し,一度停止しました.最近数ヶ月間,私はそれをリニューアルするのに時間を費やし,まだいくつかの小さな利益を得ることができます.この記事は私の高周波取引戦略といくつかの簡易化されたコードについてより詳細な紹介を提供し,議論とフィードバックの出発点として機能します.
割引口座
Binanceを例として挙げると,現在取引される10万ユニットごとに0.05%のメーカーの割引を提供しています. 日々の取引量が1億Uであれば,割引は5,000Uです. もちろん,テイカー料金は依然としてVIPレートに依存しているため,戦略が注文を受け入れる必要がない場合,VIPレベルは高周波戦略にほとんど影響しません. 異なる取引所で異なるレベルの割引が利用可能で,高い取引量が求められます. 初期には,割引なしで利益を得ることはまだありましたが,競争が激化するにつれて,割引は利益の大きな割合を占め,高周波トレーダーはトップレートを追求しました.
スピード
高周波取引は,高速な速度のためにそう呼ばれている.取引取引所のコロケーションサーバに加わり,最も低い遅延と最も安定した接続を得ることは競争条件の1つになった.戦略の内部処理時間もできるだけ短くする必要があります.この記事では,同時実行を使用する私が使用したWebSocketフレームワークを紹介します.
適した市場
高周波取引は定量取引の宝石とみなされ,多くのアルゴリズムトレーダーは試したと思いますが,ほとんどの人はお金を稼げず,改善する方法を見つけることができなかったため,やめるべきだったと思います.主な理由はおそらく間違った取引市場を選んだからです.戦略の初期段階では,比較的簡単な市場が利益を得るために取引をターゲットにされ,改善のためのフィードバックを受けなければなりません.これは戦略の進歩に有利です.あなたが最も激烈な競争力のある市場でスタートし,多くのライバルと競争するなら,あなたがどれだけ努力してもお金を失うでしょう.そしてあなたはすぐに諦めます.私は,比較的大きな取引量を持つ競争者が少ない,特に取引量が少ない,お金を稼ぐことを容易にする,新しく開始された永続契約取引ペアから始めることをお勧めします. BTCとETHは最も高い取引量と最も活発ですが,生存することも最も困難です.
競争相手と対決する
取引の市場は常に変化しており,取引戦略は一度の解決策であることはできません.これは,市場に入ることは最も賢くて勤勉なトレーダーと直接競争することを意味する高周波取引において,さらに明らかです.ゼロサムゲーム市場で,あなたが稼ぐほど,他の人々が稼ぐほど少なくなります.あなたが遅く入るほど,それは難しくなり,すでに市場に入っている人は常に改善し,いつでも排除される必要があります. 3〜4年前はおそらく最高の機会でしたが,デジタル通貨市場の活動が最近全体的に減少しているため,初心者が高周波取引を始めることは非常に困難になりました.
高周波トレード戦略には,このまたは他の取引所を通じて仲介機会を見つけ,他の取引所よりも先に注文を食べ,スピードメリットで利益を得る機会を掴むことを含む高周波仲介などいくつかの高頻度トレード戦略があります.短期トレンドから利益を得ることを含む高頻度トレンドトレード,および売買取引の両側に注文を置き,ポジションを適切に制御し,佣金割引を通じて利益を得ることを含むマーケットメーキングがあります.私の戦略はトレンドとマーケットメーキングを組み合わせ,まずトレンドを特定し,その後注文を配置し,実行後にすぐに販売し,在庫ポジションを保持しません.以下は戦略コードの紹介です.
次のコードは,バイナンス永久契約の基本アーキテクチャに基づい,主にwebsocketの深度オーダーフロー取引とポジション情報にサブスクリプトしている.市場データとアカウント情報が別々にサブスクリプトされているため,最新の情報が取得されているかどうかを判断するために,読み (-1) を継続的に使用する必要があります.ここで,EventLoop (1000) は直接デッドループを回避し,システム負荷を削減するために使用されます.EventLoop (1000) は1000msのタイムアウトでwssまたは同時タスクリートがあるまでブロックします.
var datastream = null
var tickerstream = null
var update_listenKey_time = 0
function ConncetWss(){
if (Date.now() - update_listenKey_time < 50*60*1000) {
return
}
if(datastream || tickerstream){
datastream.close()
tickerstream.close()
}
// need APIKEY
let req = HttpQuery(Base+'/fapi/v1/listenKey', {method: 'POST',data: ''}, null, 'X-MBX-APIKEY:' + APIKEY)
let listenKey = JSON.parse(req).listenKey
datastream = Dial("wss://fstream.binance.com/ws/" + listenKey + '|reconnect=true', 60)
// Symbols is the pair of symbol
let trade_symbols_string = Symbols.toLowerCase().split(',')
let wss_url = "wss://fstream.binance.com/stream?streams="+trade_symbols_string.join(Quote.toLowerCase()+"@aggTrade/")+Quote.toLowerCase()+"@aggTrade/"+trade_symbols_string.join(Quote.toLowerCase()+"@depth20@100ms/")+Quote.toLowerCase()+"@depth20@100ms"
tickerstream = Dial(wss_url+"|reconnect=true", 60)
update_listenKey_time = Date.now()
}
function ReadWss(){
let data = datastream.read(-1)
let ticker = tickerstream.read(-1)
while(data){
data = JSON.parse(data)
if (data.e == 'ACCOUNT_UPDATE') {
updateWsPosition(data)
}
if (data.e == 'ORDER_TRADE_UPDATE'){
updateWsOrder(data)
}
data = datastream.read(-1)
}
while(ticker){
ticker = JSON.parse(ticker).data
if(ticker.e == 'aggTrade'){
updateWsTrades(ticker)
}
if(ticker.e == 'depthUpdate'){
updateWsDepth(ticker)
}
ticker = tickerstream.read(-1)
}
makerOrder()
}
function main() {
while(true){
ConncetWss()
ReadWss()
worker()
updateStatus()
EventLoop(1000)
}
}
先ほど述べたように,私の高周波戦略は,まず買い売り取引を実行する前にトレンドを特定する必要があります.短期トレンドを判断するのは,主に取引データ,すなわち,方向,価格,量,取引時間を含むアグトレードサブスクリプトに基づいています.買い売り取引は主に深さと取引量を指します.以下の詳細な指標は考慮する必要があります.その多くは買い売りのために2つのグループに分けられ,特定の時間枠内で動的にカウントされます.私の戦略の時間枠は10秒以内です.
let bull = last_sell_price > avg_sell_price && last_buy_price > avg_buy_price &&
avg_buy_amount / avg_buy_time > avg_sell_amount / avg_sell_time;
let bear = last_sell_price < avg_sell_price && last_buy_price < avg_buy_price &&
avg_buy_amount / avg_buy_time < avg_sell_amount / avg_sell_time;
最新のオード価格が平均オード価格より高く, 最新のオード価格が平均オード価格より高く, 決まった間隔で購入オーダーの価値が販売オーダーの価値より高くなった場合, 短期的な上昇市場と判断されます.
function updatePrice(depth, bid_amount, ask_amount) {
let buy_price = 0
let sell_price = 0
let acc_bid_amount = 0
let acc_ask_amount = 0
for (let i = 0; i < Math.min(depth.asks.length, depth.bids.length); i++) {
acc_bid_amount += parseFloat(depth.bids[i][1])
acc_ask_amount += parseFloat(depth.asks[i][1])
if (acc_bid_amount > bid_amount && buy_price == 0) {
buy_price = parseFloat(depth.bids[i][0]) + tick_size
}
if (acc_ask_amount > ask_amount && sell_price == 0) {
sell_price = parseFloat(depth.asks[i][0]) - tick_size
}
if (buy_price > 0 && sell_price > 0) {
break
}
}
return [buy_price, sell_price]
}
ここで,必要な量に深さを繰り返す古い方法はまだ使用されています. 1秒以内に10コインで実行できる購入注文を仮定し,新しい注文の状況を考慮せずに,販売価格は購入注文が実行される位置に設定されます.
特定の時間ウィンドウのサイズを自分で設定する必要があります.
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
この比率は,最新の販売注文量に対する固定比率を表し,購入注文量に対する最新の販売注文量に対する固定比率を表します.これは,戦略が現在の購入および販売活動に応じて注文サイズを調整することを可能にします.
if(bull && (sell_price-buy_price) > N * avg_diff) {
trade('buy', buy_price, buy_amount)
}else if(position.amount < 0){
trade('buy', buy_price, -position.amount)
}
if(bear && (sell_price-buy_price) > N * avg_diff) {
trade('sell', sell_price, sell_amount)
}else if(position.amount > 0){
trade('sell', sell_price, position.amount)
}
Avg_diffは,スプレッドの平均差であり,購入と販売の差がこの値の一定倍数よりも大きく,市場が上昇している場合にのみ,購入オーダーが表示されます.ショートポジションを保持した場合,ポジションを長期にわたって保持しないためにポジションも閉鎖されます.オーダーが満たされることを保証するために,オーダーメーカーのオーダーのみを配置することができ,オーダーリターンの待機を避けるためにカスタムオーダーIDを使用できます.
var tasks = []
var jobs = []
function worker(){
let new_jobs = []
for(let i=0; i<tasks.length; i++){
let task = tasks[i]
jobs.push(exchange.Go.apply(this, task.param))
}
_.each(jobs, function(t){
let ret = t.wait(-1)
if(ret === undefined){
new_jobs.push(t)//未返回的任务下次继续等待
}
})
jobs = new_jobs
tasks = []
}
/*
tasks.push({'type':'order','param': ["IO", "api", "POST","/fapi/v1/order",
"symbol="+symbol+Quote+"&side="+side+"&type=LIMIT&timeInForce=GTX&quantity="+
amount+"&price="+price+"&newClientOrderId=" + UUID() +"×tamp="+Date.now()]})
*/