Experiência no desenvolvimento de estratégias comerciais

Autora:Bem-estar, Criado: 2019-09-05 11:49:31, Atualizado: 2023-11-07 20:48:06

img

O objetivo deste artigo é descrever alguma experiência no desenvolvimento de estratégias, bem como algumas dicas, que permitirão aos leitores entender rapidamente o ponto chave do desenvolvimento de estratégias de negociação.

Quando encontramos detalhes semelhantes em algum projeto de estratégia, podemos imediatamente encontrar uma solução razoável.

Usamos a plataforma FMZ Quant como exemplo para explicação, teste e prática.

Estratégia linguagem de programação vamos usar JavaScript

Para o alvo de negociação, tomamos o mercado de ativos blockchain (BTC, ETH, etc) como nosso objeto

Aquisição e tratamento de dados

Normalmente, dependendo da lógica da estratégia, ele pode usar as seguintes interfaces diferentes para obter dados de mercado, a maioria das lógicas de estratégia são impulsionadas por dados de mercado (é claro, algumas estratégias não se preocupam com os dados de preço, como uma estratégia de investimento fixo).

  • GetTicker: Obtenha citações em tempo real. Geralmente usado para obter rapidamente o último preço atual, Preço de compra 1, Preço de venda 1.

  • Obtenha a profundidade dos pedidos do livro de pedidos. Geralmente utilizado para obter o preço de cada camada da profundidade da carteira de ordens e o tamanho das ordens pendentes.

  • GetTrade: Obtenha o último registro de transações do mercado. Geralmente usado para analisar o comportamento do mercado em um curto ciclo de tempo e analisar mudanças microscópicas no mercado.

  • GetRecords: Obtenha dados de linha K do mercado. Geralmente usado para estratégias de rastreamento de tendências e para calcular indicadores.

Tolerância a falhas

Ao projetar a estratégia, o iniciante geralmente ignora os vários erros e acredita intuitivamente que os resultados de cada parte da estratégia são estabelecidos.

Por exemplo, algumas interfaces de mercado devolvem dados não executados:

var depth = exchange.GetDepth()

// depth.Asks[0].Price < depth.Bids[0].Price "Selling 1" price is lower than "buying 1" price, this situation cannot exist on the market.
// Because the selling price is lower than the buying price, the order must have been executed.
// depth.Bids[n].Amount = 0 Order book buying list "nth" layer, order quantity is 0
// depth.Asks[m].Price = 0 Order book selling list "mth" layer, the order price is 0

Ou diretamente exchange.GetDepth() retorna um valor nulo.

O problema é que, no que se refere ao processo de produção, o processo de fabricação é muito mais complexo do que o processo de fabricação.

A maneira normal de lidar com falhas é descartar dados e recuperá-los.

Por exemplo:

function main () {
     while (true) {
         onTick()
         Sleep(500)
     }
}

function GetTicker () {
     while (true) {
         var ticker = exchange.GetTicker()
         if (ticker.Sell > ticker.Buy) { // Take the example of fault-tolerant processing that detects whether the "Selling 1" price is less than the "Buying 1" price.
                                               // Exclude this error, the current function returns "ticker".
             Return ticker
         }
         Sleep(500)
     }
}

function onTick () {
     var ticker = GetTicker() // Make sure the "ticker" you get doesn't exist the situation that "Selling 1" price is less than the "Buying 1" price.
     // ... specific strategy logic
}

Uma abordagem semelhante pode ser utilizada para outros processos previsíveis de tolerância a falhas.

O princípio do design é que nunca se pode usar a lógica errada para conduzir a lógica estratégica.

Utilização de dados de linha K

Aquisição de dados da linha K, chamada:

var r = exchange.GetRecords()

Os dados de linha K obtidos são uma matriz, como esta:

[
    {"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
    {"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
    ...
    {"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]

Você pode ver que cada brace {} contém tempo, preço de abertura, preço mais alto, preço mais baixo, preço de fechamento e volume.

Esta é uma barra de linha K. Os dados gerais da linha K são usados para calcular indicadores como médias móveis, MACD e assim por diante.

Os dados da linha K são passados como um parâmetro (dados de matéria-prima), e então os parâmetros do indicador são definidos para calcular a função dos dados do indicador, que chamamos de função do indicador.

Há muitas funções de indicador na plataforma de negociação quantitativa FMZ Quant.

Por exemplo, calculamos o indicador da média móvel. De acordo com o ciclo dos dados da linha K, calculamos a média móvel do ciclo correspondente.

Por exemplo, os dados da linha K de passagem (uma barra de linha K representa um dia), calcula a linha média diária, a mesma coisa, se os dados da linha K da função do indicador da média de passagem for um ciclo de 1 hora, então o indicador calculado é a média móvel de 1 hora.

Normalmente, muitas vezes ignoramos um problema ao calcular o indicador. Se eu quiser calcular o indicador da média móvel de 5 dias, então primeiro preparamos os dados diários da linha K:

var r = exchange.GetRecords(PERIOD_D1) // Pass parameters to the "GetRecords" function "PERIOD_D1" specifies the day K line to be acquired.
                                       // Specific function using method can be seen at: https://www.fmz.com/api#GetRecords

Com os dados diários da linha K, podemos calcular o indicador da média móvel. Se quisermos calcular a média móvel de 5 dias, então temos que definir o parâmetro do indicador da função do indicador para 5.

var ma = TA.MA(r, 5) // "TA.MA()" is the indicator function used to calculate the moving average indicator. The first parameter sets the daily K-line data r just obtained.
                             // The second parameter is set to 5. The calculated 5-day moving average is the same as the other indicators.

Se o número de barras de linha K nos dados de linha K for menor que 5, o que podemos fazer para calcular uma média móvel válida de 5 dias?

A resposta é que não há nada que possas fazer.

Porque o indicador da média móvel é a média dos preços de fechamento de um certo número de barras de linha K.

img

Por conseguinte, antes de utilizar os dados da linha K e a função do indicador para calcular os dados do indicador, é necessário determinar se o número de barras da linha K nos dados da linha K satisfaz as condições para o cálculo do indicador (parâmetros do indicador).

Então, antes de calcular a média móvel de 5 dias, você tem que verificar primeiro.

function CalcMA () {
     var r = _C(exchange.GetRecords, PERIOD_D1) // _C() is a fault-tolerant function, the purpose is to avoid r being null, you can get more information at: https://www.fmz.com/api#_C
     if (r.length > 5) {
         Return TA.MA(r, 5) // Calculate the moving average data with the moving average indicator function "TA.MA", return it as a function return value.
     }

     Return false
}

function main () {
     var ma = CalcMA()
     Log(ma)
}

img

Exibição do teste de retorno:

[null,null,null,null,4228.7,4402.9400000000005, ... ]

Você pode ver o indicador de média móvel calculada de 5 dias. Os primeiros quatro são nulos, porque o número de barras da linha K é menor que 5, e a média não pode ser calculada. Quando você chegar à 5a barra da linha K, você pode calcula-lo.

Dicas para avaliar as atualizações da linha K

Quando escrevemos a estratégia, muitas vezes temos um cenário, como a estratégia precisa processar algumas operações quando cada ciclo de linha K é concluído, ou imprimir alguns registros.

Como implementamos essas funções? Para iniciantes que não têm experiência em programação, pode ser um problema problemático. Aqui damos as soluções.

Como julgar um ciclo de barra de linha K é concluído. Podemos começar com o atributo de tempo nos dados de linha K. Cada vez que obtermos os dados de linha K, julgaremos se o atributo de tempo da última barra de linha K desses dados de linha K está mudando ou não. Se for alterado, significa que há uma nova barra de linha K gerada (provando que o ciclo de barra de linha K anterior da barra de linha K recém-gerada foi concluído), se não houver mudança, significa que não há nova barra de linha K gerada (o último ciclo de barra de linha K atual ainda não foi concluído).

Então precisamos de uma variável para registrar o tempo da última barra de linha K dos dados de linha K.

var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // "lastTime" used to record the last K-line bar time.

Na prática, é geralmente o caso:

function main () {
     var lastTime = 0
     while (true) {
         var r = _C(exchange.GetRecords)
         if (r[r.length - 1].Time != lastTime) {
             Log ("New K-line bar generated")
             lastTime = r[r.length - 1].Time // Be sure to update "lastTime", this is crucial.

             // ... other processing logic
             // ...
         }

         Sleep(500)
     }
}

img

Pode-se ver que no backtest, o ciclo da linha K é definido para o diário (o parâmetro não é especificado quando oexchange.GetRecordsA função é chamada, e o ciclo de linha K definido de acordo com o backtest é o parâmetro padrão).

Cálculos de valor numérico

  • Calcular o tempo gasto no acesso à interface de troca

Se você quiser ter uma certa exibição ou controle sobre o tempo necessário para a estratégia acessar a interface do exchange, você pode usar o seguinte código:

function main () {
     while (true) {
         var beginTime = new Date().getTime()
         var ticker = exchange.GetTicker()
         var endTime = new Date().getTime()

         LogStatus(_D(), "GetTicker() function time-consuming:", endTime - beginTime, "millisecond")
         Sleep(1000)
     }
}

Simplificando, o carimbo horário gravado após ligar para oGetTickerA função é subtraída da marca de tempo antes da chamada, e o número de milissegundos experimentados é calculado, ou seja, o tempo tomado peloGetTickerfunção da execução para retornar.

  • Use Math.min / Math.max para limitar os limites superior e inferior do valor

Por exemplo, no processo de colocação de uma ordem de venda, o montante da ordem de venda não deve ser superior ao número de moedas na conta. Porque se for maior do que o número de moedas disponíveis na conta, a ordem causará erros.

Controlamos assim:

Por exemplo, nós planejamos vender curto 0.2 moedas.

var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)

Isto garante que o número de encomendas não exceda o número de moedas disponíveis na conta.

Pela mesma razão,Math.maxÉ utilizada para assegurar o limite inferior de um valor.

  • Para que tipo de cena se aplica isso?

Geralmente, a troca normal tem um limite mínimo de envio de ordens para certos pares comerciais. Se for inferior ao valor mínimo, a ordem será rejeitada. Isso também causará o fracasso do programa.

Supondo que o BTC normalmente tenha uma quantidade mínima de ordem de colocação de 0,01.

Estratégias de negociação podem, por vezes, resultar em menos de 0,01 quantidades de ordem, para que possamos usarMath.maxpara assegurar a quantidade mínima de encomenda.

  • Quantidade de encomenda, controlo da precisão dos preços

A precisão pode ser controlada através do_N()função ouSetPrecision function.

OSetPrecision()A função só precisa de ser definida uma vez, e o número de casas decimais na quantidade de ordem e valor do preço é automaticamente truncado no sistema.

O_N()A função é realizar truncamento de ponto decimal (controle de precisão) para um determinado valor.

Por exemplo:

var pi = _N(3.141592653, 2)
Log(pi)

O valor de pi é truncado pela casa decimal, e 2 casas decimais são reservados, que é: 3.14

Ver a documentação da API para mais detalhes.

Algumas configurações lógicas

  • Temporização, executar algumas operações durante um determinado período de tempo

Você pode usar esse mecanismo para usar o método de detecção de carimbo de tempo para determinar o carimbo de tempo atual menos o carimbo de tempo da última vez que a tarefa programada foi executada, e calcular o tempo decorrido em tempo real. Quando o tempo decorrido exceder um determinado comprimento de tempo definido. Depois disso, uma nova operação é executada.

Por exemplo, utilizado numa estratégia de investimento fixo.

var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // number of milliseconds a day
function main () {
     while (true) {
         var nowTime = new Date().getTime()
         if (nowTime - lastActTime > waitTime) {
             Log ("Execution Fixed")
             // ... specific fixed investment operation, buying operation.


             lastActTime = nowTime
         }

         Sleep(500)
     }
}

Este é um exemplo simples.

  • Conceber um mecanismo de recuperação automática para a estratégia

Utilizando o FMZ Quant_G()Função, e saindo da função de salvamento, é conveniente conceber uma estratégia para sair do progresso de salvamento e reiniciar o estado de recuperação automática.

var hold = {
     Price : 0,
     Amount : 0,
}

function main () {
     if (_G("hold")) {
         var ret = _G("hold")
         hold.price = ret.price
         hold.amount = ret.amount
         Log("restore hold:", hold)
     }

     var count = 1
     while (true) {
         // ... strategy logic
         // ... In the strategy operation, it is possible that when opening a position, then assign the position price of the open position to "hold.price", and the amount of open positions is assigned to "hold.amount" to record the position information.

         hold.price = count++ // simulate some values
         hold.amount = count/10 // Simulate some values

         Sleep(500)
     }
}

function onexit () { // Click the stop button on the robot to trigger the execution of this function. After the execution, the robot stops.
     _G("hold", hold)
     Log("save hold:", JSON.stringify(hold))
}

img

Os dados obtidos noholdObjeto é salvo cada vez que o robô é parado. e quando cada vez que os dados são reiniciados, os dados são lidos e o valor doholdé restaurado ao estado anterior à parada.

Naturalmente, o acima é um exemplo simples. se for usado em uma estratégia de negociação real, ele deve ser projetado de acordo com os dados-chave que precisam ser restaurados na estratégia (geralmente são informações de conta, posição, valor de lucro, direção de negociação e assim por diante.).

Além disso, pode também definir algumas outras condições para restaurar.

Estas são algumas dicas para desenvolver uma estratégia de negociação, e espero que possa ajudar os iniciantes!

Treinamento prático é a maneira mais rápida de melhorar a si mesmo! Desejo a todos boa sorte.


Relacionados

Mais.