
[TOC] 2020年に高頻度戦略を紹介する記事を書きました(https://www.fmz.com/digest-topic/6228)。多くの注目を集めたにもかかわらず、深く書かれていませんでした。 2年以上が経過し、市場は変化しました。その記事が公開された後、私の高頻度取引戦略は長い間安定して利益を上げることができましたが、徐々に利益が減少し、一時は止まってしまうこともありました。ここ数か月、私は改装に多大な労力を費やし、現在はいくらかの収入を得ることができています。この記事では、高頻度戦略といくつかの簡略化されたコードに関する私のアイデアを詳しく紹介します。これらは議論の出発点として役立ちます。どなたでもコミュニケーションを取り、フィードバックをいただければ幸いです。
リベートを受け取るアカウントの場合、Binance を例にとると、現在のメーカーリベートは 100,000 の 0.5% です。1 日の取引量が 1 億 U の場合、リベートは 5,000 U になります。もちろん、テイカー手数料は依然として VIP レートに基づいているため、注文を取る必要がない戦略の場合、VIP レベルは高頻度戦略にほとんど影響を与えません。一般的に、取引所のレベルによってリベート率が異なり、より高い取引量を維持する必要があります。昔、一部の通貨の市場が大きく変動していたときは、リベートがなくても利益がありました。内部循環の激化に伴い、リベートが利益の大きな割合を占め、完全にリベートに依存するようになりました。高頻度取引業者は、最高料金。
スピード。高頻度戦略が高頻度と呼ばれる理由は、非常に高速だからです。最も低いレイテンシと最も安定した接続を得るために取引所のコロケーションサーバーに参加することも、内部循環の条件の 1 つになっています。戦略の内部時間消費も可能な限り低くする必要があります。この記事では、並行実行を使用する私が使用している Websocket フレームワークを紹介します。
適切な市場。高頻度取引は定量取引の宝石として知られています。多くのプログラマティックトレーダーが試したことがあると思いますが、ほとんどの人はお金を稼げず、改善する方法が見つからないため、おそらくやめてしまいます。主な理由はおそらく彼らは間違った方向を探している。取引市場。戦略の初期段階では、比較的簡単に取引して利益をあげられる市場を探す必要があります。そうすることで、利益と改善に関するフィードバックが得られ、戦略の前進につながります。最も競争の激しい市場でスタートし、多くの潜在的なライバルと競争すると、どれだけ努力してもお金を失い、長く持ちこたえることはできなくなります。新しく上場された無期限契約の取引ペアをお勧めします。現時点では、特に取引量が比較的多いため、競争相手はそれほど多くありません。これは、お金を稼ぐのが最も簡単な時期です。 BTC と ETH は取引量が最も多く、最も活発なトランザクションを誇りますが、生き残るのが最も難しい通貨でもあります。
競争に正面から立ち向かってください。あらゆる取引市場は動的に変化しています。一度で永遠に機能する取引戦略は存在しません。これは高頻度取引ではさらに顕著です。この市場に参入するということは、最も賢く勤勉なトレーダーのグループと直接競争することを意味します。ゼロサム市場では、あなたが稼ぐ金額が増えるほど、他の人の稼ぐ金額は少なくなります。参入が遅れれば遅れるほど、困難になります。すでに市場に参入している人も、いつ淘汰されるかわからないので、改善を続けなければなりません。 3、4年前が最高のチャンスだったはずです。最近、デジタル通貨市場全体の活動は低下しており、初心者が高頻度取引を行うことは非常に困難になっています。
高頻度戦略には多くの種類がある
私の戦略はトレンドとマーケットメーカーの組み合わせです。まずトレンドを判断して注文を出し、取引が完了したらすぐに売り注文を出します。在庫ポジションは保持しません。戦略コードは下記に紹介します。
以下のコードは、Binance 永久契約の基本アーキテクチャに基づいており、主に Websocket の深度注文フロー取引市場情報とポジション情報をサブスクライブします。マーケット情報とアカウント情報は別々に購読されるため、最新の情報を取得しているかどうかを判断するためにread(-1)を継続的に使用する必要があります。ここでEventLoop(1000)を使用するのは、直接の無限ループを回避し、システム負荷を軽減するためです。 EventLoop(1000) は、wss または同時タスクが返されるまでブロックされ、タイムアウトは 1000 ミリ秒です。
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()
}
//需要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是设定的交易对
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)
}
}
前述したように、私の高頻度戦略では、売買を実行する前にトレンドを判断する必要があります。短期的なトレンドは主に、サブスクリプション内の各取引、つまりaggTradeの取引方向、価格、数量、取引時間などを含む取引データに基づいて判断されます。売買の主な基準は、深さと取引量です。以下は、注意が必要なインジケーターの詳細な紹介です。ほとんどのインジケーターは、買いと売りの 2 つのグループに分かれており、特定の時間枠で動的にカウントされます。私の戦略の時間枠は 10 秒以内です。
//bull代表短期看涨,bear短期看跌
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]
}
ここでは、古いアイデアを採用し、必要な量まで深さを繰り返します。ここでは、10コインの買い注文が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は平均市場価格の差です。買い注文は、ビッド・アスク・スプレッドがこの値の特定の倍数よりも大きく、トレンドが強気の場合にのみ発注されます。ショート注文を保有している場合、ポジションは注文の長期保留を避けるため、この時点では閉鎖されます。保留中の注文が確実に実行されるように、メーカーのみの注文を出すことができます。また、Binance のカスタム注文 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 = []
}
/*
需要的任务参数写在param里
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()]})
*/