Sistema de backtesting de alta frecuencia basado en transacción por transacción y defectos del backtesting de línea K

El autor:- ¿ Por qué?, Creado: 2022-11-30 12:38:50, Actualizado: 2023-09-11 20:01:01

img

Sistema de backtesting de alta frecuencia basado en transacción por transacción y defectos del backtesting de línea K

He publicado un motor de backtesting en el artículo Estudio sobre la estrategia de cobertura de divisas múltiples de Binance Futures (https://www.fmz.com/digest-topic/5584El primer informe se basa en la prueba de retroceso de la línea K de una hora, que verifica la eficacia de la estrategia. Sin embargo, de hecho, el tiempo de sueño de la estrategia pública es de 1s, que es una estrategia de frecuencia bastante alta. Obviamente, es imposible obtener resultados precisos utilizando la prueba de retroceso de la línea K de una hora.https://www.fmz.com/digest-topic/5621Como resultado, los retornos de backtesting han mejorado mucho, pero todavía es imposible determinar qué parámetros deben usarse en el caso del segundo nivel, y la comprensión de toda la estrategia no es muy clara.

Problemas basados en pruebas de retroceso de la línea K

En primer lugar, ¿cuál es la línea K histórica? Los datos de una línea K incluyen cuatro precios: el precio más alto, el precio de apertura, el precio más bajo y el precio de cierre, el tiempo de inicio, el tiempo de finalización y la cantidad de operaciones de intervalo. La mayoría de las plataformas y marcos cuantitativos se backtestan en base a la línea K, y la plataforma FMZ Quant también proporciona backtesting de nivel Tick. La velocidad de backtesting de la línea K es muy rápida, y no es un problema en la mayoría de los casos, pero también hay defectos muy graves, especialmente la estrategia de múltiples variedades y la estrategia de alta frecuencia de backtesting, que difícilmente puede sacar conclusiones correctas.

En primer lugar, es una cuestión de tiempo. El tiempo del precio más alto y más bajo de los datos de la línea K no se da, por lo que es innecesario considerar, pero los precios de apertura y cierre más importantes no son los tiempos de apertura y cierre de la posición. Incluso si las variedades comerciales son impopulares, a menudo no se negocian durante más de diez segundos. Cuando hacemos pruebas de retroceso de múltiples variedades de estrategias, a menudo predeterminamos que sus precios de apertura y cierre son los mismos, lo que también es la base de la prueba de retroceso del precio de cierre.

Imagínese usar la línea de minutos para probar el arbitraje de dos variedades. La diferencia entre ellas es generalmente de 10 yuanes. Ahora se encuentra que a las 10:01, el precio de cierre del contrato A es de 100 yuanes, el precio de cierre del contrato B es de 112 yuanes, y la diferencia es de 12 yuanes. Así que la estrategia comienza a cubrirse. En un cierto momento, la diferencia regresa, y la estrategia gana 2 yuanes de ganancias de retorno.

Sin embargo, la situación real puede ocurrir a las 10:00:45, el contrato A generó una transacción de 100 yuanes, y luego no hubo transacción. El contrato B generó una transacción de 112 yuanes a las 10:00:58. A las 10:01, ambos precios no existían. ¿Cuál era el precio de apertura en este momento? ¿Y qué diferencia podría obtener la cobertura? No lo sabemos. Una situación posible es que a las 10:00:58, la tendencia de compra y venta de uno del contrato A es de 101.9-102.1, y no hay un spread de 2 yuanes en absoluto, lo que inducirá a error nuestra optimización de la estrategia.

El segundo es el emparejamiento. El emparejamiento real es el precio y el tiempo primero. Si el comprador excede el precio de venta de uno, él / ella generalmente concluirá la transacción al precio de venta de uno, de lo contrario, él / ella entrará en el libro de pedidos y esperará.

El último es el impacto de la transacción de la estrategia en sí misma en el mercado. Si se trata de una pequeña prueba de retroceso de fondos, el impacto será pequeño. Sin embargo, si la cantidad de operaciones representa una gran proporción, tendrá un impacto en el mercado. No solo el punto de deslizamiento del precio será grande cuando la transacción se complete inmediatamente, sino que si su orden de compra se completa en la prueba de retroceso, en realidad previene la transacción de otros operadores originales que desean comprar, lo que tendrá un impacto de efecto mariposa en el mercado. Sin embargo, este impacto no se puede cuantificar, y solo se puede decir por experiencia que el comercio de alta frecuencia solo puede acomodar pequeños fondos.

Pruebas de retroceso basadas en profundidad y tick en tiempo real

FMZ proporciona la backtesting de nivel de bot real, que puede obtener la profundidad histórica real de 20 niveles, tick de segundo en tiempo real, transacción por transacción y otros datos, y basándose en esto, ha hecho la función de reproducción de bot real (https://www.fmz.com/m/databaseEste tipo de medición de backtest tiene una gran cantidad de datos y una velocidad lenta, que solo se puede usar durante dos días. Para estrategias que son de frecuencia relativamente alta o requieren un juicio de tiempo estricto, es necesario el backtesting de bot real. Los pares comerciales y el tiempo recopilados por FMZ no son demasiado largos, pero hay más de 70 mil millones de piezas de datos históricos. El mecanismo de coincidencia actual es que si la orden de compra es mayor que la orden de venta, se igualará completamente inmediatamente sin mirar la cantidad, y si la orden de compra es menor que la orden de venta, entrará en la cola de coincidencia. Este mecanismo de backtesting resuelve los dos primeros problemas de backtesting de la línea K, pero aún no puede resolver el último problema. Y debido a que la cantidad de datos es demasiado grande, la velocidad y el rango de backtest son limitados.

img img

Mecanismo de pruebas de retroceso basado en el flujo de pedidos transacción por transacción

Hay muy poca información sobre la K-Line, y la profundidad también puede ser falsa. Sin embargo, un tipo de datos es la intención real de transacción del mercado, que refleja el historial de transacciones más real, es decir, transacción por transacción. En este artículo, propondré un sistema de backtesting de alta frecuencia basado en el flujo de pedidos, que reducirá en gran medida la cantidad de datos en la backtesting a nivel de bot real, y hasta cierto punto, simulará el impacto del volumen de operaciones en el mercado.

He descargado la transacción por transacción de los últimos 5 días Binance XTZ contrato perpetuo (dirección de descarga:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csvEn la actualidad, la mayoría de los datos de los Estados miembros son de una variedad diferente, con un total de 213.000 datos.

[['XTZ', 1590981301905, 2.905, 0.4, 'False\n'],
 ['XTZ', 1590981303044, 2.903, 3.6, 'True\n'],
 ['XTZ', 1590981303309, 2.903, 3.7, 'True\n'],
 ['XTZ', 1590981303738, 2.903, 238.1, 'True\n'],
 ['XTZ', 1590981303892, 2.904, 0.1, 'False\n'],
 ['XTZ', 1590981305250, 2.904, 0.1, 'False\n'],
 ['XTZ', 1590981305643, 2.903, 197.3, 'True\n'],

Los datos son una lista bidimensional, ordenada por tiempo de transacción. Los significados específicos son: nombre de especie, precio de transacción, sello de tiempo de transacción, cantidad de transacción y si la orden de venta se ejecuta activamente. Hay tanto compra como venta. Cada transacción incluye al comprador y al vendedor. Si el comprador es un creador de mercado y el vendedor es un tomador de transacción activo, los últimos datos serán Verdaderos.

En primer lugar, de acuerdo con la dirección de la transacción, podemos especular sobre la compra y la venta en el mercado con precisión. Si es una orden de venta activa, el precio de compra en este momento es el precio de la transacción. Si es una orden de compra activa, el precio de venta es el precio de la transacción. Si hay una nueva transacción, actualizaremos la nueva posición de apertura. Si no se actualiza, se conservará el último resultado. Es fácil lanzar el último momento de los datos anteriores. El precio de compra es 2.903 y el precio de venta es 2.904.

De acuerdo con el flujo de órdenes, se puede emparejar de la siguiente manera: tomar una orden de compra como ejemplo, el precio es el precio, y la cantidad de orden es la cantidad. En este momento, comprar una y vender una de las posiciones de apertura son ofertas y pedidos respectivamente. Si el precio es menor que el pedido y mayor que el pedido, se juzgará como el fabricante primero, y se puede dar prioridad al emparejamiento. Luego, todas las transacciones con un precio de transacción menor o igual al precio durante la vida de la orden se emparejarán con este pedido (si el precio es menor o igual a la oferta, no se puede dar prioridad a la transacción, y las órdenes con un precio de transacción menor que el precio se emparejarán con este pedido). El precio de emparejamiento es el precio, y la cantidad de transacción es la cantidad de transacciones de transacción por transacción, hasta que la orden se cierre por completo o sea cancelada. Si la diferencia es mayor que el precio, se considerará que es un tomador.

Es fácil ver el problema de este emparejamiento. Si la orden es un tomador, la situación real es que la transacción se puede hacer de inmediato, en lugar de esperar a que una nueva orden coincida con ella. En primer lugar, no consideramos el número de órdenes listadas en el mercado. Incluso si hubiera datos, el juicio directo de la transacción ha cambiado la profundidad e afectado al mercado. El emparejamiento basado en nuevas órdenes es equivalente a reemplazar sus órdenes con los pedidos reales en la historia, que no superarán el límite de cantidad de transacción del mercado en sí mismo en ningún caso, y la ganancia final no puede exceder el beneficio máximo generado por el mercado.

Si el precio de compra de una orden es igual al precio de compra de una, todavía hay una cierta probabilidad de que la orden se iguale al precio de compra de una.

Código de búsqueda de pareja

Los objetos de intercambio pueden referirse a la introducción al principio, básicamente sin cambios. Solo se agrega la diferencia entre las comisiones del creador y el tomador, y la velocidad de backtesting se optimiza.

    symbol = 'XTZ'
    loop_time = 0
    intervel = 1000 #The sleep time of the strategy is 1000ms
    init_price = data[0][2] #Initial price
    e = Exchange([symbol],initial_balance=1000000,maker_fee=maker_fee,taker_fee=taker_fee,log='') #Initialize the exchange
    depth = {'ask':data[0][2], 'bid':data[0][2]} #depth
    order = {'buy':{'price':0,'amount':0,'maker':False,'priority':False,'id':0},
             'sell':{'price':0,'amount':0,'maker':False,'priority':False,'id':0}} #Order
    for tick in data:
        price = int(tick[2]/tick_sizes[symbol])*tick_sizes[symbol] #Transaction price
        trade_amount = tick[3] #Number of transactions
        time_stamp = tick[1] #Transaction timestamp
        if tick[4] == 'False\n':
            depth['ask'] = price
        else:
            depth['bid'] = price
        
        if depth['bid'] < order['buy']['price']:
            order['buy']['priority'] = True
        if depth['ask'] > order['sell']['price']:
            order['sell']['priority'] = True
        if price > order['buy']['price']:
            order['buy']['maker'] = True
        if price < order['sell']['price']:
            order['sell']['maker'] = True
        
        #Order network delay can also be used as one of the matching conditions, which is not considered here
        cond1 = order['buy']['priority'] and order['buy']['price'] >= price and order['buy']['amount'] > 0
        cond2 = not order['buy']['priority'] and order['buy']['price'] > price and order['buy']['amount'] > 0
        cond3 = order['sell']['priority'] and order['sell']['price'] <= price and order['sell']['amount'] > 0
        cond4 = not order['sell']['priority'] and order['sell']['price'] < price and order['sell']['amount'] > 0

        if cond1 or cond2:
            buy_price = order['buy']['price'] if order['buy']['maker'] else price
            e.Buy(symbol, buy_price, min(order['buy']['amount'],trade_amount), order['buy']['id'], order['buy']['maker'])
            order['buy']['amount'] -= min(order['buy']['amount'],trade_amount)
            e.Update(time_stamp,[symbol],{symbol:price})
        if cond3 or cond4:
            sell_price = order['sell']['price'] if order['sell']['maker'] else price
            e.Sell(symbol, sell_price, min(order['sell']['amount'],trade_amount), order['sell']['id'], order['sell']['maker'])
            order['sell']['amount'] -= min(order['sell']['amount'],trade_amount)
            e.Update(time_stamp,[symbol],{symbol:price})

        if time_stamp - loop_time > intervel:
            order = get_order(e,depth,order) #Trading logic, not given here
            loop_time += int((time_stamp - loop_time)/intervel)*intervel

Hay que tener en cuenta algunos detalles:

-1. Cuando hay una nueva transacción, debemos hacer coincidir el pedido primero, y luego colocar el pedido de acuerdo con el último precio. -2. Cada orden tiene dos atributos: creador si es creador, y prioridad prioridad de emparejamiento. Tomando la orden de compra como ejemplo, cuando el precio de compra es menor que el precio de venta, se marca como creador, y cuando el precio de compra es mayor que el precio de compra, se marca como emparejamiento prioritario. La prioridad determina si coincide si el precio es igual al precio de compra, y el creador determina la comisión. -3. El creador y la prioridad de la orden se actualizan. Por ejemplo, hay una orden de compra grande que excede las posiciones de apertura, cuando hay un precio mayor que el precio de compra, en este momento, la cantidad de transacción restante será creador. -4. Es necesario el intervalo de la estrategia, que puede representar el retraso del mercado.

Pruebas de retroceso de las estrategias de red

Finalmente, llegamos a la etapa de backtesting real. Aquí, vamos a backtest una estrategia de cuadrícula más clásica para ver si ha logrado el efecto esperado. El principio de la estrategia es que cada vez que el precio aumente en un 1%, mantendremos un cierto valor de órdenes de posición corta (de lo contrario, mantendremos órdenes de posición larga), y calcularemos la orden de compra y la orden de venta y las suspenderemos por adelantado. El código no se publicará. Encapsular todos los códigos en la funciónGrid ('XTZ ', 100,0.31000, maker_fee=-0.00002, taker_fee=0.0003)Los parámetros son: par de operaciones, valor de tenencia con desviación de precio del 1%, densidad de órdenes del 0,3%, intervalo de espera de ms, comisión de orden pendiente y comisión de toma.

El mercado de XTZ ha estado en shock en los últimos 5 días, lo cual es muy adecuado para la estrategia de red.

img

En primer lugar, vamos a hacer pruebas de retrospectiva del impacto de las diferentes posiciones en los rendimientos.

e1 = Grid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e1.account['USDT'])
e2 = Grid('XTZ',1000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e2.account['USDT'])
e3 = Grid('XTZ',10000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e3.account['USDT'])
e4 = Grid('XTZ',100000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e4.account['USDT'])

Un total de cuatro grupos fueron sometidos a pruebas de retroceso con valores de posición de 100, 1000, 10000 y 100000, y el tiempo total de pruebas de retroceso fue de 1,3 segundos.

{'realised_profit': 28.470993031132966, 'margin': 0.7982662957624465, 'unrealised_profit': 0.0104554474048441, 'total': 10000028.481448, 'leverage': 0.0, 'fee': -0.3430967859046398, 'maker_fee': -0.36980249726699727, 'taker_fee': 0.026705711362357405}
{'realised_profit': 275.63148945320177, 'margin': 14.346335829979132, 'unrealised_profit': 4.4382117331794045e-14, 'total': 10000275.631489, 'leverage': 0.0, 'fee': -3.3102045933457784, 'maker_fee': -3.5800688964477048, 'taker_fee': 0.2698643031019274}
{'realised_profit': 2693.8701498889504, 'margin': 67.70120400534114, 'unrealised_profit': 0.5735269329348516, 'total': 10002694.443677, 'leverage': 0.0001, 'fee': -33.984021415250744, 'maker_fee': -34.879233866850974, 'taker_fee': 0.8952124516001403}
{'realised_profit': 22610.231198585603, 'margin': 983.3853688758861, 'unrealised_profit': -20.529965947304365, 'total': 10022589.701233, 'leverage': 0.002, 'fee': -200.87094000385412, 'maker_fee': -261.5849078470078, 'taker_fee': 60.71396784315319}

Se puede ver que las ganancias realizadas finales son 28.4%, 27.5%, 26.9% y 22.6% del valor de la posición respectivamente. Esto también está en línea con la situación real. Cuanto mayor sea el valor de la posición, mayor será el valor del pedido, y más probable será que ocurran transacciones parciales. Las ganancias realizadas finales serán más pequeñas en relación con el monto del pedido.

img

También podemos hacer pruebas de retroceso del impacto de diferentes parámetros en el retorno de la prueba de retroceso, como la densidad de pedidos pendientes, el tiempo de sueño y las comisiones.

{'realised_profit': 29.079440803790423, 'margin': 0.7982662957624695, 'unrealised_profit': 0.0104554474048441, 'total': 10000029.089896, 'leverage': 0.0, 'fee': -0.3703702128662524, 'maker_fee': -0.37938946377435134, 'taker_fee': 0.009019250908098965}

El beneficio ha aumentado un poco. Esto se debe a que solo un grupo de pedidos está pendiente de la estrategia, y algunos pedidos no pueden obtener el precio fluctuante porque no tienen tiempo para cambiar. El tiempo de sueño reducido mejora este problema. Esto también muestra la importancia de los pedidos pendientes de varios grupos en la estrategia de la red.

Resumen de las actividades

Este documento propone un nuevo sistema de backtesting basado en el flujo de pedidos de manera innovadora, que puede simular en parte la situación de coincidencia como orden pendiente, toma de pedidos, transacción parcial y retraso, en parte refleja el impacto del volumen de fondos estratégicos en los rendimientos, y tiene un importante valor de referencia para estrategias de alta frecuencia y estrategias de cobertura.


Relacionados

Más.