Sistema de backtest de alta frequência baseado em cada transação e os defeitos do backtest de linha K

Autora:Bem-estar, Criado: 2020-06-16 10:30:19, Atualizado: 2023-11-01 20:26:21

img

Quando escreviPesquisa sobre Binance Futures Multi-currency Hedging Strategy, Eu também lancei um mecanismo de backtest. E o primeiro relatório foi baseado no backtest de uma hora da linha K, que verificou a eficácia da estratégia. Mas o tempo de sono da estratégia de código aberto real é 1 segundo, que é uma estratégia de freqüência bastante alta. Obviamente, usar o backtest de linha K por hora não pode produzir resultados precisos. Mais tarde, os resultados do backtest da linha K de nível minuto foram adicionados, e a receita do backtest melhorou muito, mas ainda é impossível determinar quais parâmetros devem ser usados no caso do nível segundos, e a compreensão de toda a estratégia não é muito clara. A principal razão é a importante desvantagem do backtest baseado na linha K.

Problemas baseados no backtest da linha K

Primeiro de tudo, o que é a linha histórica K? Um dado de linha K contém quatro preços de alto, aberto, baixo, fechado, os dois primeiros vezes e o volume do intervalo. A maioria das plataformas e estruturas de quantização são baseadas em backtest de linha K, e a plataforma FMZ também fornece backtest de nível de tick. A velocidade de backtest da linha K é muito rápida e, na maioria dos casos, tem muito poucos problemas, mas também tem defeitos muito sérios, especialmente a estratégia de multi-variedade de backtest e a estratégia de alta frequência, é quase impossível tirar uma conclusão correta.

A primeira é a questão do tempo. O tempo do preço mais alto e o preço mais baixo dos dados da linha K não são dados e não precisam ser considerados, mas os preços de abertura e fechamento mais importantes não são o tempo de abertura e fechamento.

Imagine usar a linha de nível de minuto K para testar a arbitragem de duas variedades. A diferença entre elas geralmente é de 10 yuans (ou dólares). Agora, às 10:01, o preço de fechamento do contrato A é de 100, o contrato B é de 112 e a diferença é de 12 yuans. Então a estratégia começa a cobrir. Em certo momento, a diferença de preço retornou e a estratégia fez um lucro de retorno de 2 yuans.

Mas a situação real pode ser que às 10:00:45, o contrato A produziu uma transação de 100 yuans, após o qual não houve transação, o contrato B teve uma transação de 112 yuans às 10:00:58, às 10:01:00 Ambos os preços não existem. Qual é o preço de mercado neste momento, e quanto a operação de hedge pode obter?101.9para102.1Isto irá enganar muito a nossa estratégia de otimização.

O segundo é o problema de correspondência. A correspondência real é a prioridade de preço e prioridade de tempo. Se o comprador exceder o preço Vender 1, ele geralmente negociará diretamente no preço Vender 1, caso contrário, ele entrará no livro de pedidos pendente e esperará. Os dados da linha K obviamente não têm preço Comprar 1 e preço Vender 1, é impossível simular o nível de correspondência de preço detalhado.

O último é o impacto da própria estratégia no mercado. Se for um backtest de fundos de pequena quantidade, o impacto não é grande. Mas se o volume de transações for grande, ele terá um impacto no mercado. Não só o deslizamento de preço será grande quando você colocar uma ordem de grande volume, se você comprar uma ordem longa executada, esse tipo de ação realmente capturar as ordens de outros comerciantes que originalmente queriam comprar, o efeito batterfly terá um impacto no mercado. Este efeito não pode ser quantificado. Só podemos dizer pela experiência que a negociação de alta frequência só pode acomodar pequenos fundos.

Backtest baseado em profundidade e tick em tempo real

FMZ fornece backtest de nível real, que pode obter real histórico20 layer depth price, segundo nível em tempo realTicks, Each Individual TransactionCom base nessas características, o FMZ criou uma função de reprodução de transacções em tempo real.

Este tipo de quantidade de dados de backtest é muito grande, e a velocidade de backtest também é muito lenta, geralmente só pode backtestar por dois dias.

O mecanismo de correspondência atual é que, se a ordem de compra for maior do que o Vender 1, ela será completamente correspondida imediatamente sem olhar para o valor, e se for menor do que o Vender 1, ela entrará na fila de correspondência para esperar. Tal mecanismo de backtest resolve os dois primeiros problemas do backtest da linha K, mas ainda não pode resolver o último problema. E porque a quantidade de dados é muito grande, a velocidade e o intervalo de tempo do backtest são limitados.

img

Mecanismo de backtest baseado no fluxo de transacções ordem por ordem

Há muito pouca informação na linha K, e a profundidade do preço também pode ser uma profundidade falsa, mas há um tipo de dados que é a vontade de transação real do mercado, que reflete o histórico de transações mais real, ou seja,Each Individual TransactionEste artigo propõe um sistema de backtest de alta frequência baseado no fluxo de pedidos, que reduzirá consideravelmente o volume de dados de backtest de nível de mercado real e, em certa medida, simulará o impacto do volume de negociação no mercado.

Eu baixei a transação dos últimos 5 dias Binance XTZ contrato perpétuo (endereço de download:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv), como uma variedade não popular, tem um total de 213000 dados de transacções, primeiro vamos olhar para a composição dos dados:

[['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'],

Os dados são uma lista bidimensional, ordenada em ordem cronológica. Os significados específicos são os seguintes: nome da variedade, preço da transação, carimbo de tempo da transação, quantidade da transação, se é uma transação ativa de ordem de venda. Há lado de compra e venda, e cada transação inclui o comprador e o vendedor.Makere o vendedor é um activoTaker, os últimos dados sãoTrue.

Em primeiro lugar, de acordo com a direção da transação, você pode especular com bastante precisão sobre o Buy 1 e Sell 1 no mercado. Se for uma ordem de venda ativa, então o preço Buy 1 neste momento é o preço da transação, se for uma ordem de compra ativa, o preço Sell 1 será o preço da transação. Se houver uma nova transação, então todo o preço será renovado e atualizado. O último resultado será mantido se não houver renovação e atualização. É fácil introduzir o último momento dos dados acima, o preço Buy 1 é 2.903, e o Sell 1 é 2.904.

De acordo com o fluxo de pedidos, pode ser combinado desta forma: tomar uma ordem de compra como exemplo, o preço éprice, a quantidade da encomenda éamount, então comprar e vender 1 neste momento sãobideaskrespectivamente.priceé inferior aaske superior abid, então é julgado comomakerprimeiro, e prioridade pode ser combinado para fazer um negócio, em seguida, todos os negócios com um preço de transação inferior ou igual aopricedurante o tempo de existência da ordem será correspondida com esta ordem (sepriceé inferior ou igual abid, não é dada prioridade à transacção.pricesão combinados com esta ordem.)

O preço correspondente é:price, e o volume é o volume de transacções deEach Individual Transaction, até que a encomenda seja completada ou cancelada.ask, é considerada umatakerDepois disso, durante o tempo em que a ordem existe, todos os negócios com um preço de transacção inferior ou igual apricesão correspondidas com esta ordem, e o preço de correspondência é o preço de transacção da ordemEach Individual TransactionA distinção entremakeretakerA maior parte das estratégias de alta frequência são baseadas em um sistema de negociação de preços, que consiste basicamente no incentivo das ordens pendentes e em descontos nas taxas de transacção.

É fácil ver um problema com este tipo de correspondência.taker, a situação real é que ele pode ser executado imediatamente, em vez de esperar por uma nova ordem para ser correspondida com ele. em primeiro lugar, não consideramos o volume de ordens pendentes, mesmo que haja alguns dados, julgar diretamente a transação também mudou a profundidade do preço, afetando o mercado.

Com base na correspondência de novas ordens, é equivalente a substituir as ordens existentes no histórico com suas ordens. Em qualquer caso, não excederá o limite do próprio volume de negociação do mercado e o lucro final não pode exceder o lucro máximo gerado pelo mercado. Parte do mecanismo de correspondência também afeta o volume de ordens, que por sua vez afeta a receita da estratégia, refletindo quantitativamente a capacidade da estratégia. Não haverá backtest tradicional, quando a quantidade de fundos duplica e o ganho duplica.

Ainda há alguns pequenos detalhes. Se o preço de compra da ordem for igual a Buy 1, ainda há uma certa probabilidade de que o preço de compra seja igual a Buy 1, este tipo de situação não será considerado aqui.

Código correspondente

Os objetos de troca podem referir-se à introdução no início, basicamente inalterada, somente adicionando a diferença entremakeretakerA seguir, introduziremos principalmente o código de correspondência.

 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] # executed price
        trade_amount = tick[3] # executed volume
        time_stamp = tick[1] # executed 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, 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

Alguns pormenores a notar:

  • Quando há uma nova transação, devemos primeiro combinar a ordem, e depois colocar a ordem de acordo com o último preço.

  • Cada ordem tem dois atributos: makerse trata de um maker, prioritymatching priority, tomando uma ordem de compra como exemplo, quando o preço de compra é inferior a Sell 1, é marcada comomaker, e quando o preço de compra for superior a Buy 1, é assinalado comoPriority matching, priorityDetermina se o preço é igual ou não ao preço de compra, e o fabricante determina a taxa de transação.

  • OmakereprioritySe uma grande compra for efectuada e exceder a capacidade de mercado, quando um preço for superior ao preço de compra, o volume remanescente será o volume de compra total.maker.

  • EstratégiaintervalO que é necessário, pode representar o atraso do mercado.

Testes de retrocesso da estratégia de rede

Finalmente, é a fase de backtest real. Vamos backtest uma das estratégias de grade mais clássico aqui para ver se podemos alcançar os resultados esperados. O princípio da estratégia é que cada vez que o preço sobe 1%, mantemos uma ordem curta de um certo valor (inversamente, mantemos uma ordem longa), calcular a ordem de compra e venda de antemão.Grid('XTZ', 100, 0.3, 1000, maker_fee=-0.00002, taker_fee=0.0003)função, os parâmetros são: par de negociação, desvio de preço do valor de detenção de 1%, densidade de ordens pendentes é 0,3%, intervalo de sonoms, comissões de ordens pendentes e comissões de ordens executadas.

O preço de mercado do XTZ esteve em choque nos últimos 5 dias, o que é muito adequado para redes.

img

Em primeiro lugar, verificamos o efeito das diferentes posições de detenção sobre o retorno dos lucros.

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'])

Um total de quatro grupos foram testados, o valor das posições de detenção foi de 100, 1000, 10000, 100 000, e o tempo total de backtest foi de 1,3 s. Os resultados são os seguintes:

{'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}

Pode-se ver que os lucros realizados finais são de 28,4%, 27,5%, 26,9% e 22,6% do valor da posição de detenção, respectivamente. Isso também está de acordo com a situação real. Quanto maior o valor da posição de detenção, maior o valor da ordem pendente, maior a probabilidade de uma transação parcial ocorrer, e menor o ganho realizado final em relação ao valor da ordem pendente.

img

Também podemos testar o impacto de diferentes parâmetros na receita do backtest, como densidade de pedidos pendentes, tempo de sono, taxa de transação, etc. Pegue o tempo de sono como exemplo, mude-o para 100ms e compare o tempo de sono para 1000ms para ver o retorno do lucro.

{'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}

A redução do tempo de sono melhora este problema, o que também ilustra a importância da estratégia de rede para a colocação de múltiplos conjuntos de ordens.

Resumindo

Este artigo propõe de forma inovadora um novo sistema de backtest baseado no fluxo de ordens, que pode simular parcialmente a situação de correspondência de ordens pendentes, ordens executadas, ordens executadas parcialmente, atrasos, etc., e reflete parcialmente o impacto da quantidade de fundos de estratégia nas receitas. Para estratégias de alta frequência e hedging, Ele tem um valor de referência importante.


Relacionados

Mais.