
[TOC] Escribí un artículo en 2020 presentando estrategias de alta frecuencia, https://www.fmz.com/digest-topic/6228. Aunque recibió mucha atención, no fue escrito en profundidad. Han pasado más de dos años y el mercado ha cambiado. Después de publicar ese artículo, mi estrategia de alta frecuencia logró generar dinero de manera constante durante mucho tiempo, pero las ganancias disminuyeron gradualmente e incluso se detuvieron en un momento. En los últimos meses he dedicado mucho esfuerzo a la renovación y actualmente estoy consiguiendo ganar algo de dinero. En este artículo, presentaré mis ideas sobre estrategias de alta frecuencia y algunos códigos simplificados con más detalle, que pueden servir como punto de partida para el debate. Todos son bienvenidos a comunicarse y brindar comentarios.
Para las cuentas que reciben reembolsos, tomando a Binance como ejemplo, el reembolso actual para creadores es del 0,5 % de 100 000. Si el volumen de transacciones diarias es de 100 millones de U, el reembolso será de 5000 U. Por supuesto, la tarifa del tomador todavía se basa en la tarifa VIP, por lo que si la estrategia no requiere tomar órdenes, el nivel VIP tendrá poco impacto en las estrategias de alta frecuencia. Generalmente, los diferentes niveles de intercambio tienen diferentes tasas de reembolso y requieren mantener un mayor volumen de transacciones. Hace mucho tiempo, cuando el mercado de algunas divisas fluctuaba mucho, aún había ganancias incluso sin descuentos. Con la intensificación de la circulación interna, los descuentos representaron una gran proporción de los beneficios, e incluso dependían completamente de los descuentos. Los traders de alta frecuencia buscan Las mejores tarifas.
velocidad. La razón por la que la estrategia de alta frecuencia se llama alta frecuencia es porque es muy rápida. Unirse al servidor colo del exchange para obtener la latencia más baja y la conexión más estable también se ha convertido en una de las condiciones para la circulación interna. El consumo de tiempo interno de la estrategia también debe ser lo más bajo posible. En este artículo, presentaré el marco de trabajo de WebSocket que utilizo, que utiliza ejecución concurrente.
El mercado adecuado. El trading de alta frecuencia se conoce como la joya del trading cuantitativo. Creo que muchos traders programáticos lo han probado, pero la mayoría de la gente probablemente deja de hacerlo porque no gana dinero y no encuentra una forma de mejorar. La razón principal es probablemente que están buscando el camino equivocado. El mercado comercial. En la etapa inicial de una estrategia, uno debe buscar mercados en los que sea relativamente fácil ganar dinero operando, de modo que haya ganancias y retroalimentación sobre las mejoras, lo que favorecerá el avance de la estrategia. Si empiezas en el mercado más competitivo y compites con muchos rivales potenciales, perderás dinero por mucho que lo intentes y no podrás aguantar más. Recomiendo los nuevos pares de negociación con contratos perpetuos que se han incluido en la lista. En este momento, no hay tantos competidores, especialmente cuando el volumen de negociación es relativamente grande. Este es el momento más fácil para ganar dinero. BTC y ETH tienen el mayor volumen comercial y las transacciones más activas, pero también son las más difíciles de sobrevivir.
Enfrentar la competencia directamente. Cualquier mercado de trading cambia de forma dinámica. Ninguna estrategia de trading puede funcionar de una vez por todas. Esto es aún más evidente en el trading de alta frecuencia. Entrar en este mercado significa competir directamente contra un grupo de los traders más inteligentes y diligentes. En un mercado de suma cero, cuanto más ganas, menos ganan los demás. Cuanto más tarde entres, más difícil será. Los que ya están en el mercado también deben seguir mejorando, ya que pueden ser eliminados en cualquier momento. Hace tres o cuatro años debería haber sido la mejor oportunidad. Recientemente, la actividad general del mercado de divisas digitales ha disminuido y ahora es muy difícil para los novatos participar en operaciones de alta frecuencia.
Existen muchos tipos de estrategias de alta frecuencia.
Mi estrategia es una combinación de estrategia de tendencia y de creación de mercado. Primero determino la tendencia, luego coloco una orden y, de inmediato, coloco una orden de venta después de que se complete la transacción. No mantengo posiciones de inventario. El código de la estrategia se presenta a continuación.
El siguiente código se basa en la arquitectura básica de los contratos perpetuos de Binance y se suscribe principalmente a la información del mercado y a la información de posición de las transacciones de flujo de órdenes de profundidad websocket. Dado que la información del mercado y la información de la cuenta se suscriben por separado, es necesario utilizar read(-1) continuamente para determinar si se obtiene la información más reciente. EventLoop(1000) se utiliza aquí para evitar un bucle infinito directo y reducir la carga del sistema. EventLoop(1000) se bloqueará hasta que wss o las tareas simultáneas regresen, con un tiempo de espera de 1000 ms.
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)
}
}
Como se mencionó anteriormente, mi estrategia de alta frecuencia requiere determinar la tendencia antes de ejecutar compras y ventas. La tendencia a corto plazo se juzga principalmente en función de los datos de transacción de cada transacción, es decir, aggTrade en la suscripción, que incluye la dirección de la transacción, el precio, la cantidad, el tiempo de la transacción, etc. Las principales referencias para comprar y vender son la profundidad y el volumen de negociación. A continuación se presenta una introducción detallada de los indicadores que requieren atención. La mayoría de los indicadores se dividen en dos grupos: compra y venta, y se cuentan de forma dinámica en una determinada ventana de tiempo. La ventana de tiempo de mi estrategia es de 10 segundos.
//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;
Si el último precio de venta es mayor que el precio de venta promedio, el último precio de compra es mayor que el precio de compra promedio y el valor de la orden de compra de intervalo fijo es mayor que el valor de la orden de venta, entonces se considera que es alcista a corto plazo. . Por el contrario, es bajista.
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]
}
Aquí seguimos adoptando la vieja idea e iteramos la profundidad hasta la cantidad requerida. Aquí asumimos que una orden de compra de 10 monedas se puede ejecutar en 1 segundo. Sin considerar nuevas órdenes pendientes, el precio de la orden de venta se establece en la posición donde la Se realizará una orden de compra de 10 monedas. Debes configurar tú mismo el tamaño de la ventana de tiempo específica.
let buy_amount = Ratio * avg_sell_amount / avg_sell_time
let sell_amount = Ratio * avg_buy_amount / avg_buy_time
Ratio significa relación fija, lo que significa que la cantidad de la orden de compra es una relación fija de la cantidad de la orden de venta más reciente. Esta estrategia puede ajustar de forma adaptativa el tamaño del pedido en función de la actividad de compra y venta actual.
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 ellos, avg_diff es la diferencia del precio medio del mercado. Solo se colocará una orden de compra cuando el diferencial entre oferta y demanda sea mayor que un determinado múltiplo de este valor y la tendencia sea alcista. Si mantiene una orden corta, la posición También estará cerrado en este momento para evitar la retención prolongada del pedido. Puede colocar una orden de solo creador para garantizar que se ejecute la orden pendiente. Además, puedes utilizar el ID de orden personalizado de Binance, por lo que no tendrás que esperar a que se devuelva la orden.
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()]})
*/