Eu escrevi um artigo em 2020 introduzindo estratégias de negociação de alta frequência (https://www.fmz.com/digest-topic/6228Apesar de ter recebido alguma atenção, não foi muito aprofundado. Dois anos e meio se passaram desde então, e o mercado mudou. Depois que publiquei esse artigo, minha estratégia de alta frequência foi capaz de gerar lucros estáveis por um longo tempo, mas os lucros diminuíram gradualmente e até pararam em um ponto. Nos últimos meses, passei tempo renovando-a, e ainda pode gerar alguns pequenos lucros.
Contas de reembolso da Comissão
usando a Binance como exemplo, atualmente oferecem um desconto do fabricante de 0,05% para cada 100.000 unidades negociadas. Se o volume de negociação diário for de 100 milhões de U, o desconto é de 5.000 U. Claro, a taxa de aceitação ainda depende da taxa VIP, então se a estratégia não precisar aceitar ordens, o nível VIP tem pouco efeito sobre a estratégia de alta frequência. Diferentes níveis de desconto de comissão estão disponíveis em diferentes exchanges, exigindo um alto volume de negociação. Nos primeiros dias, ainda havia lucro a ser feito sem descontos, mas à medida que a concorrência se intensificava, os descontos representavam uma proporção maior dos lucros e os traders de alta frequência buscavam as maiores taxas.
Velocidade
A negociação de alta frequência é chamada assim por causa de sua velocidade rápida. Juntar-se a um servidor de colocação de exchange de negociação e obter a menor latência e conexão mais estável tornou-se uma das condições de concorrência. O tempo de processamento interno da estratégia também deve ser o menor possível.
Mercado adequado
A negociação de alta frequência é considerada a jóia na coroa da negociação quantitativa, e acredito que muitos comerciantes algorítmicos a tentaram, mas a maioria das pessoas deveria ter parado porque não conseguiu ganhar dinheiro e não conseguiu encontrar uma maneira de melhorar. A principal razão é provavelmente porque eles escolheram o mercado de negociação errado. Na fase inicial da estratégia, mercados relativamente fáceis devem ser direcionados para negociação para obter lucros e receber feedback para melhoria, o que é propício ao progresso da estratégia. Se você começar no mercado mais ferozmente competitivo e competir com muitos oponentes, você perderá dinheiro não importa o quanto você tente, e você desistirá rapidamente. Eu recomendo começar com pares de negociação de contratos perpétuos recém-lançados, onde há menos concorrentes, especialmente aqueles com volumes de negociação relativamente grandes, tornando mais fácil ganhar dinheiro. BTC e ETH têm os volumes mais altos e mais ativos, mas também são os mais difíceis de sobreviver no potencial.
Enfrentar a concorrência de frente
O mercado para qualquer negociação está em constante mudança, e nenhuma estratégia de negociação pode ser uma solução de uma só vez. Isso é ainda mais óbvio na negociação de alta frequência, onde entrar no mercado significa competir diretamente com os comerciantes mais inteligentes e mais diligentes. Em um mercado de jogo de soma zero, quanto mais você ganha, menos os outros ganham. Quanto mais tarde você entra, mais difícil fica, e aqueles que já estão no mercado devem melhorar constantemente e podem ser eliminados a qualquer momento. Três ou quatro anos atrás foi provavelmente a melhor oportunidade, mas com o recente declínio geral da atividade no mercado de moeda digital, tornou-se muito difícil para os iniciantes começar a fazer negociação de alta frequência.
Existem várias estratégias de negociação de alta frequência, como a arbitragem de alta frequência, que envolve encontrar oportunidades de arbitragem através desta ou de outras bolsas, aproveitando a oportunidade de comer ordens à frente de outras e obter lucros com vantagem de velocidade; a negociação de tendência de alta frequência, que envolve lucrar com tendências de curto prazo; e a criação de mercado, que envolve a colocação de ordens em ambos os lados dos negócios de compra e venda, controlando bem as posições e ganhando lucros através de descontos de comissão. Minha estratégia combina tendência e criação de mercado, primeiro identificando tendências e depois colocando ordens, vendendo imediatamente após a execução e não mantendo posições de estoque. Abaixo está uma introdução ao código de estratégia.
O código a seguir baseia-se na arquitetura básica do contrato perpétuo da Binance e subscreve principalmente as transações de fluxo de pedidos de profundidade do websocket e as informações de posição. Como os dados de mercado e as informações da conta são assinados separadamente, a leitura (-1) precisa ser usada continuamente para determinar se as informações mais recentes foram obtidas. Aqui, o EventLoop (1000) é usado para evitar loops mortos diretos e reduzir a carga do sistema. O EventLoop (1000) bloqueia até que haja um retorno de tarefa wss ou simultâneo, com um timeout de 1000ms.
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)
}
}
Como mencionado anteriormente, minha estratégia de alta frequência requer primeiro identificar tendências antes de executar transações de compra e venda. Julgar tendências de curto prazo é baseado principalmente em dados de transação, ou seja, o aggTrade assinado, que inclui a direção, preço, quantidade e tempo de transação. As transações de compra e venda se referem principalmente à profundidade e ao volume de transações. A seguir estão indicadores detalhados que precisam ser considerados, a maioria dos quais são divididos em dois grupos para compra e venda e são contados dinamicamente dentro de uma determinada janela de tempo. Minha janela de tempo de estratégia é dentro de 10 segundos.
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;
Se o último preço de compra for superior ao preço de venda médio e o último preço de oferta for superior ao preço médio de oferta e o valor da ordem de compra for superior ao valor da ordem de venda num intervalo fixo, considera-se que se trata de um mercado de alta a curto prazo.
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]
}
Supondo que uma ordem de compra que pode ser executada por 10 moedas dentro de 1 segundo e sem considerar a situação de novas ordens, o preço de venda é definido na posição onde a ordem de compra
O tamanho da janela de tempo precisa ser definido por si mesmo.
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
O rácio representa uma proporção fixa da quantidade da última ordem de venda, representando a quantidade da ordem de compra como uma proporção fixa da quantidade da última ordem de venda.
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)
}
Entre eles, o avg_diff é a diferença média no spread, e somente quando a diferença de compra e venda na colocação de ordens for maior do que um certo múltiplo deste valor e o mercado estiver em alta, uma ordem de compra será colocada.
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()]})
*/