Eu lancei um mecanismo de backtesting no artigo
Primeiro de tudo, o que é a linha histórica K? Um dado da linha K inclui quatro preços: o preço mais alto, o preço de abertura, o preço mais baixo e o preço de fechamento, o tempo de início, o tempo de término e a quantidade do intervalo de negociação. A maioria das plataformas e estruturas quantitativas são testadas com base na linha K, e a plataforma FMZ Quant também fornece backtesting de nível Tick. A velocidade do backtesting da linha K é muito rápida, e não é um problema na maioria dos casos, mas também há falhas muito sérias, especialmente a estratégia multi-variedade e a estratégia de alta frequência do backtesting, que dificilmente podem tirar conclusões corretas.
Em primeiro lugar, é uma questão de tempo. O tempo do preço mais alto e mais baixo dos dados da linha K não é dado, por isso é desnecessário considerar, mas os preços de abertura e fechamento mais importantes não são os tempos de abertura e fechamento da posição. Mesmo que as variedades de negociação sejam impopulares, elas geralmente não são negociadas por mais de dez segundos.
Imagine usar a linha do minuto para testar a arbitragem de duas variedades. A diferença entre elas é geralmente de 10 yuans. Agora, fica claro que às 10:01, o preço de fechamento do contrato A é de 100 yuans, o preço de fechamento do contrato B é de 112 yuans e a diferença é de 12 yuans. Então a estratégia começa a se proteger. Em certo momento, a diferença retorna e a estratégia ganha 2 yuans de lucros de retorno.
No entanto, a situação real pode acontecer às 10:00:45, o contrato A gerou uma transação de 100 yuans, e então não houve transação. O contrato B gerou uma transação de 112 yuans às 10:00:58. Às 10:01, ambos os preços não existiam. Qual era o preço de abertura neste momento? E quanta diferença poderia obter a cobertura? Não sabemos. Uma situação possível é que às 10:00:58, a tendência de compra de um e venda de um do contrato A é de 101.9-102.1, e não há spread de 2 yuans, o que enganará muito nossa otimização de estratégia.
O segundo é o matchmaking. O matchmaking real é o preço e o tempo primeiro. Se o comprador exceder o preço de venda, ele / ela geralmente concluirá a transação no preço de venda, caso contrário, ele / ela entrará no livro de pedidos e esperará.
O último é o impacto da transação da própria estratégia no mercado. Se for um backtest de fundo pequeno, o impacto será pequeno. No entanto, se a quantidade de negociação representar uma grande proporção, ele terá um impacto no mercado. Não só o ponto de queda de preço será grande quando a transação for concluída imediatamente, mas se sua ordem de compra for concluída no backtest, ele realmente antecipa a transação de outros traders originais que querem comprar, o que terá um impacto de efeito borboleta no mercado. No entanto, esse impacto não pode ser quantificado e só pode ser dito pela experiência que a negociação de alta frequência só pode acomodar pequenos fundos.
FMZ fornece o real bot nível backtesting, que pode obter o real histórico de 20 níveis de profundidade, em tempo real segundo tick, transação por transação e outros dados, e com base nisso, ele fez o real bot função de reprodução (https://www.fmz.com/m/databaseEste tipo de medição de backtest tem uma grande quantidade de dados e uma velocidade lenta, que só pode ser usada por dois dias. Para estratégias que são de frequência relativamente alta ou exigem um julgamento de tempo rigoroso, o backtesting de bot real é necessário. Os pares de negociação e o tempo coletados pela FMZ não são muito longos, mas há mais de 70 bilhões de peças de dados históricos. O mecanismo de correspondência atual é que, se a ordem de compra for maior que a ordem de venda, ela será completamente correspondida imediatamente sem olhar para a quantidade, e se a ordem de compra for menor que a ordem de venda, ela entrará na fila de correspondência. Este mecanismo de backtesting resolve os dois primeiros problemas da backtesting da linha K, mas ainda não pode resolver o último problema. E porque a quantidade de dados é muito grande, a velocidade e o tempo de backtest são limitados.
No entanto, um tipo de dados é a intenção real de transação do mercado, refletindo o histórico de transações mais real - ou seja, transação por transação. Neste artigo, proponho um sistema de backtesting de alta frequência baseado no fluxo de pedidos, que reduzirá muito a quantidade de dados no backtesting no nível real do bot e, até certo ponto, simulará o impacto do volume de negociação no mercado.
Eu baixei a transação por transação dos últimos 5 dias
[['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 por tempo de transação. Os significados específicos são: nome da espécie, preço da transação, carimbo de tempo da transação, quantidade da transação e se a ordem de venda é ativamente executada. Há compra e venda. Cada transação inclui o comprador e o vendedor.
Em primeiro lugar, de acordo com a direção da transação, podemos especular a compra e venda no mercado com precisão. Se for uma ordem de venda ativa, o preço de compra neste momento é o preço da transação. Se for uma ordem de compra ativa, o preço de venda é o preço da transação. Se houver uma nova transação, atualizaremos a nova posição de abertura. Se não for atualizada, o último resultado será mantido. É fácil lançar o último momento dos dados acima. O preço de compra é 2.903 e o preço de venda é 2.904.
De acordo com o fluxo de ordens, ele pode ser combinado da seguinte forma: tomar uma ordem de compra como exemplo, o preço é o preço, e a quantidade da ordem é a quantidade. Neste momento, comprar uma e vender uma das posições de abertura são propostas e pedir, respectivamente. Se o preço for menor do que a oferta e maior do que a oferta, ele será julgado como o fabricante primeiro, e a prioridade pode ser dada ao matchmaking. Então todas as transações com um preço de transação menor ou igual ao preço durante a vida da ordem serão combinadas com essa ordem (se o preço for menor ou igual à oferta, não pode ser dada prioridade à transação, e as ordens com um preço de transação menor do que o preço serão combinadas com essa ordem). A existência da compra é preço, e a quantidade da transação é a quantidade de transações por transação, até que a transação seja completamente encerrada ou cancelada. Se a diferença for maior do que o preço, ele será considerado um tomador.
É fácil ver o problema desse matchmaking. Se a ordem é um tomador, a situação real é que a transação pode ser feita imediatamente, em vez de esperar por uma nova ordem para combiná-la. Em primeiro lugar, não consideramos o número de ordens listadas no mercado. Mesmo que houvesse dados, o julgamento direto da transação mudou a profundidade e afetou o mercado. O matchmaking baseado em novas ordens é equivalente a substituir suas ordens com as ordens reais no histórico, que não excederá o limite de quantidade de transação do próprio mercado em qualquer caso, e o lucro final não pode exceder o lucro máximo gerado pelo mercado.
Há também alguns pequenos detalhes. Se o preço de compra de uma ordem for igual ao preço de compra, ainda há uma certa probabilidade de que a ordem seja correspondida ao preço de compra. A prioridade da ordem e a probabilidade de transação precisam ser consideradas, o que é mais complexo e não será considerado aqui.
Os objetos de troca podem se referir à introdução no início, basicamente inalterada. Somente a diferença entre as comissões do fabricante e do tomador é adicionada, e a velocidade de backtesting é otimizada.
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
Alguns pormenores devem ser observados:
-1. Quando há uma nova transação, devemos combinar a ordem primeiro, e depois colocar a ordem de acordo com o último preço.
-2. Cada ordem tem dois atributos: maker
Finalmente, chegamos ao estágio de backtesting real. Aqui, vamos testar uma estratégia de grade mais clássica para ver se ela alcançou o efeito esperado. O princípio da estratégia é que cada vez que o preço aumenta em 1%, manteremos um certo valor de ordens de posição curta (caso contrário, manteremos ordens de posição longa), e calcularemos a ordem de compra e venda e pendê-los antecipadamente. O código não será liberado. Encapsular todos os códigos na funçãoGrid ('XTZ ', 100,0.31000, maker_fee=-0.00002, taker_fee=0.0003)
Os parâmetros são: par de negociação, valor de detenção com desvio de preço de 1%, densidade de ordens de 0,3%, intervalo de espera de ms, comissão de ordem pendente e comissão de tomador.
O mercado da XTZ esteve em choque nos últimos 5 dias, o que é muito adequado para a estratégia da rede.
Os rendimentos medidos pelo mecanismo tradicional de backtesting certamente aumentarão proporcionalmente ao aumento das posições.
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 com valores de posição de 100, 1000, 10000 e 100000, e o tempo total de backtesting 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, respectivamente. Isso também está de acordo com a situação real. Quanto maior o valor da posição, maior será o valor da ordem, e mais prováveis serão as transações parciais. Os retornos realizados finais serão menores em relação ao valor da ordem.
Também podemos fazer backtest sobre o impacto de diferentes parâmetros no retorno do backtest, como a densidade de pedidos pendentes, tempo de sono e comissões.
{'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}
O lucro aumentou um pouco. Isso ocorre porque apenas um grupo de pedidos está pendente para a estratégia, e alguns pedidos não podem obter o preço flutuante porque não têm tempo para mudar. O tempo de sono reduzido melhora esse problema. Isso também mostra a importância de pedidos pendentes de vários grupos na estratégia da rede.
Este artigo propõe um novo sistema de backtesting baseado no fluxo de pedidos de forma inovadora, que pode em parte simular a situação de correspondência, como pedido pendente, tomada de ordem, transação parcial e atraso, em parte reflete o impacto do volume de fundo estratégico nos retornos, e tem um importante valor de referência para estratégias de alta frequência e estratégias de hedging.