Ich schrieb 2020 einen Artikel, in dem ich Hochfrequenzstrategien vorstellte.https://www.fmz.com/bbs-topic/9750. Obwohl es ziemlich viel Aufmerksamkeit erhielt, war es nicht sehr tiefgründig. Seitdem sind mehr als zwei Jahre vergangen, und der Markt hat sich geändert. Nachdem dieser Artikel veröffentlicht wurde, konnte meine Hochfrequenzstrategie für eine lange Zeit stabil Gewinne erzielen, aber allmählich gingen die Gewinne zurück und hörten sogar an einem Punkt auf. In den letzten Monaten habe ich einige Anstrengungen unternommen, um sie zu überarbeiten, und jetzt kann sie noch einige Gewinne erzielen. In diesem Artikel werde ich eine detailliertere Einführung in meine Hochfrequenzstrategieideen und einen vereinfachten Code als Ausgangspunkt für Diskussionen geben; und Feedbacks sind willkommen.
Binance hat einen Maker-Rebat von 0,0005%. Wenn der tägliche Transaktionsbetrag 100 Millionen U beträgt, beträgt der Rabatt 5000 U. Natürlich basieren die Taker-Gebühren immer noch auf VIP-Raten, so dass, wenn die Strategie keine Taker benötigt, das VIP-Niveau wenig Einfluss auf Hochfrequenzstrategien hat. Verschiedene Ebenen der Börsen haben im Allgemeinen unterschiedliche Rabattraten und erfordern die Aufrechterhaltung eines hohen Transaktionsbetrags. In den frühen Zeiten, in denen einige Währungsmärkte stark schwankten, gab es auch ohne Rabatte Gewinne.
Schnelligkeit. Der Grund, warum Hochfrequenzstrategien als Hochfrequenz bezeichnet werden, ist, dass sie sehr schnell sind. Der Anschluss an den Colo-Server des Austauschs, die Erlangung der niedrigsten Latenzzeit und der stabilsten Verbindung ist auch zu einer der Bedingungen für den internen Wettbewerb geworden. Die interne Verbrauchszeit der Strategie sollte so gering wie möglich sein, und in diesem Artikel werde ich das von mir verwendete Websocket-Framework vorstellen, das eine gleichzeitige Ausführung annimmt.
Der Grund sollte sein, dass sie den falschen Handelsmarkt gewählt haben. In der Anfangsphase der Strategieentwicklung sollten relativ einfache Märkte ausgewählt werden, um Gewinne im Handel zu erzielen, so dass Gewinne und Feedback für die Verbesserung vorhanden sind, was dem Fortschritt der Strategie förderlich ist. Wenn Sie mit vielen potenziellen Gegnern auf dem wettbewerbsfähigsten Markt konkurrieren, werden Sie, egal wie hart Sie es versuchen, bald Geld verlieren und aufgeben. Ich empfehle neu gelistete Perpetual Contract-Trading-Paare, wenn es nicht so viele Konkurrenten gibt, insbesondere solche mit relativ großer Transaktionsmenge; dies ist, wenn Gewinne am einfachsten sind. BTC und ETH haben die größte Transaktionsmenge und sind am aktivsten in Transaktionen, aber auch am schwierigsten zu überleben.
Der Markt für jede Transaktion verändert sich ständig, und keine Handelsstrategie kann ewig andauern, besonders im Hochfrequenzhandel. In diesen Markt einzutreten bedeutet, direkt mit den klügsten und fleißigsten Händlern zu konkurrieren. In einem Nullsummenspielmarkt verdienen Sie je mehr, desto weniger werden andere verdienen. Je später Sie eintreten, desto höher ist die Schwierigkeit; diejenigen, die bereits auf dem Markt sind, müssen sich auch kontinuierlich verbessern. Vor 3-4 Jahren war wahrscheinlich die beste Gelegenheit; vor kurzem ist die allgemeine Aktivität in den digitalen Währungsmärkten zurückgegangen, was es für Neulinge sehr schwierig macht, jetzt mit dem Hochfrequenzhandel zu beginnen.
Es gibt verschiedene Hochfrequenzstrategien:
Der folgende Code basiert auf dem grundlegenden Rahmen von Binance-Perpetual Contracts, der hauptsächlich Websocket-Tiefe, Tiefen-Orderfluss-Marktdaten und Positionsinformationen abonniert. Da die Marktdaten und Kontoinformationen getrennt abonniert werden, ist es notwendig, read ((-1) kontinuierlich zu verwenden, um festzustellen, ob die neuesten Informationen erhalten wurden. Hier wird EventLoop ((1000) verwendet, um direkte endlose Schleifen zu vermeiden und die Systembelastung zu reduzieren. EventLoop ((1000) blockiert, bis es wss oder gleichzeitige Aufgabenrückkehr mit einem Timeout von 1000ms gibt.
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 are the set trading pairs
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)
}
}
Wie bereits erwähnt, erfordert meine Hochfrequenzstrategie die Bestimmung des Trends vor der Ausführung von Kauf und Verkauf. Der kurzfristige Trend wird hauptsächlich anhand von Tic-by-Tic-Transaktionsdaten beurteilt, dh dem AggTrade im Abonnement, das Transaktionsrichtung, Preis, Menge, Transaktionszeit usw. umfasst. Kauf und Verkauf beziehen sich hauptsächlich auf Tiefe und Handelsbetrag. Im Folgenden finden Sie detaillierte Einführungen in die zu berücksichtigenden Indikatoren; die meisten von ihnen sind in Kauf- und Verkaufsgruppen unterteilt und werden innerhalb eines bestimmten Zeitfensters dynamisch gezählt. Das Zeitfenster meiner Strategie liegt innerhalb von 10 Sekunden.
//bull represents short-term bullish, bear represents short-term bearish
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;
Wenn der letzte Verkaufspreis höher als der durchschnittliche Verkaufspreis ist, der letzte Kaufpreis höher als der durchschnittliche Kaufpreis ist und der Wert der Bestellung im festen Intervall größer ist als der Wert der Bestellung, dann wird es als kurzfristig bullisch beurteilt.
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]
}
Hier nehmen wir immer noch den alten Ansatz an und iterieren bis zur erforderlichen Tiefe. Angenommen, dass 10 Münzen in 1 Sekunde gehandelt werden können, ohne neue ausstehende Aufträge zu berücksichtigen, wird der Verkaufspreis an der Position festgelegt, an der 10 Münzen gekauft werden. Die spezifische Größe des Zeitfensters muss von Ihnen selbst festgelegt werden.
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
Das Ratio stellt einen festen Anteil dar, was bedeutet, dass die Kaufbestellmenge ein fester Anteil an der jüngsten Verkaufsbestellmenge ist.
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)
}
Bei avg_diff handelt es sich um die durchschnittliche Marktpreisdifferenz, und ein Kauf Auftrag wird nur erteilt, wenn der Bid-Ask-Spread größer als ein bestimmtes Vielfaches dieses Wertes ist und es bullisch ist. Wenn eine Short-Position gehalten wird, wird die Position auch geschlossen, um eine längere Haltung zu vermeiden. Die Aufträge können als Only-Maker-Orders platziert werden, um sicherzustellen, dass sie ausgeführt werden. Darüber hinaus kann die benutzerdefinierte Order-ID von Binance verwendet werden, so dass es nicht notwendig ist, auf die Orderantwort zu warten.
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)//Unreturned tasks will continue to wait next time
}
})
jobs = new_jobs
tasks = []
}
/*
Write the required task parameters in 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()]})
*/