Continuando com os dois artigos anteriores sobre assuntos que necessitam de atenção na negociação programática:

Produtos secos pesados ​​- detalhes e reflexões sobre negociação automatizada em tempo real de sistemas de negociação quantitativos (1. Problemas e dificuldades)

Sistema de negociação quantitativo - Detalhes e reflexões automatizadas da oferta firme (2. Objetivo da oferta firme)

Aqui continuaremos a falar sobre algumas habilidades detalhadas de manipulação no design de código.

base de dados

Conforme mencionado nos dois artigos anteriores, o registro do status de uma combinação de estratégias mais complexas é muito importante e requer o uso de um banco de dados.

Na verdade, a maioria dos negócios de programação pode ser classificada na categoria CRUD, ou seja, o processo principal consiste apenas em adicionar, excluir, modificar e verificar o banco de dados. Os códigos comerciais não são exceção. O comportamento comercial do Mercado secundário de média e baixa frequência não é Muito diferente de quando as pessoas vão às compras num determinado tesouro.O foco Da negociação automatizada reside Na gestão do estatuto estratégico.

Se você ainda não está familiarizado com bancos de dados relacionais, como SQL, é recomendável aprender diretamente a usar bancos de dados em memória NoSQL, como Redis. Suas vantagens são que é fácil de começar, tem excelente desempenho e é inerentemente de thread único. Não há necessidade de considerar operações de baixo nível, como bloqueio de dados durante a leitura e gravação. No entanto, a desvantagem é que não existe uma chave primária e funções como o incremento automático de números não podem ser realizadas. Se necessário, você mesmo deverá escrever código para implementá-lo. No entanto, nunca usei essa função em negociações automatizadas por tanto tempo.

Outra questão sem importância é que, para bancos de dados na memória, se a quantidade de dados que precisam ser armazenados for relativamente grande, a memória do servidor deverá ser maior. No entanto, para negociação quantitativa geral, 4G ou mesmo 2G de memória é suficiente e é. não é necessário.

O Redis é realmente poderoso no mundo real. Se sua estratégia tiver requisitos de alta frequência, ele também poderá implementar o modelo de assinatura de mensagens pub/sub, eliminando a necessidade de adicionar vários módulos MQ adicionais. Se você usa um banco de dados relacional como o MySQL, conforme mencionado anteriormente, desde que não o tenha usado na prática antes, terá que investir tempo e energia no aprendizado de SQL, uma linguagem de programação relativamente nojenta e difícil. Observe que todos estão aqui para fazer negociações quantitativas, não para aprender a escrever código. Tente usar uma solução mais simples.

Além disso, se você deseja construir um centro de mercado, considerando o desempenho, você também utilizará o banco de dados Redis e cooperará com a fila de mensagens por conveniência. A solução é simples e adequada para a maioria dos cenários. Um servidor (ou vários servidores) pode ser usado.

projeto de código

Basicamente, ao escrever código, a estrutura de dados central é o mais importante. O design da estrutura de dados não é razoável o suficiente e o código será escrito de maneira inadequada, pois inevitavelmente fará com que os vários módulos sejam acoplados, tornando as modificações muito problemáticas. Mas para projetá-lo corretamente, você precisa de alguma experiência comercial, ou seja, experiência empresarial. Trata-se de como escrever código, não há muito a dizer. As estratégias de cada pessoa são diferentes e não podem ser tratadas igualmente. No entanto, alguns princípios são provavelmente os mesmos.

Por exemplo, após o envio de uma ordem, não há garantia de sucesso, mesmo que seja uma ordem de mercado, pois pode ser rejeitada pela bolsa. Por exemplo, a central está muito ocupada ou ocorre perda temporária de pacotes de dados da rede. Então, é melhor projetar alguns estados intermediários semelhantes ao 2 Phase Commit. Se falhar, continue tentando (é claro, não com muita frequência, caso contrário, será banido se exceder o limite. Existe um decorador de nova tentativa especial para fazer isso). ou adicione mais métodos A para corrigir o estado de erro.

Em circunstâncias normais, mesmo que a bolsa caia, ela se recuperará em pouco tempo. Se você não se recuperar, uma das partes ficará completamente inativa, seja a exchange ou seu próprio servidor de código. Isso precisa ser monitorado e alertado. É impossível que o código automatizado cubra tudo. Se não puder ser processado automaticamente, será necessária uma intervenção manual. Se o processamento manual não for tratado a tempo, desde que não continuemos a abrir posições para aumentar a exposição, o problema não será muito grande, porque existem ordens algorítmicas de stop-loss para cobrir o fundo.

A seguir está uma lógica de abertura de estratégia que eu mesmo resolvi:

Basicamente, ao desenhar um fluxograma semelhante, o código ficará muito mais organizado e o número de possíveis bugs será reduzido. Portanto, antes de escrever o código, é melhor desenhar um fluxograma ou resolvê-lo durante a escrita, para que a lógica fique clara e você tenha uma ideia.

A lógica de fechar uma posição é muito mais complicada do que isto, porque existem mais condições para fechar uma posição, tais como stop-profit, stop-loss e sinais de saída de várias estratégias, que irão desencadear o fecho da posição. Em suma, a importância da saída é maior do que a da entrada, e a oferta real também é um pouco mais problemática.

Como a lógica estratégica de cada pessoa é definitivamente diferente, não entrarei em detalhes aqui. O principal é endireitar sua lógica estratégica e evitar que diversos erros possíveis aconteçam.

No entanto, eles parecem complicados e são apenas um monte de if elses (o Python nem sequer tem uma instrução switch). Se as condições forem atendidas, prossiga para a próxima etapa. Caso contrário, saia.

controle do tempo

Ao fazer transações, o tempo é sem dúvida muito importante. Aqui vou compartilhar mais algumas de minhas habilidades de controle de tempo.

Na programação geral, se houver tarefas que precisam ser executadas regularmente, geralmente são usados ​​​​pacotes de tarefas cron, como o apscheduler. Por exemplo, planeje uma tarefa agendada para detectar se o preço da linha k precisa abrir ou fechar uma posição assim que chegar a hora.

Na verdade, esse tipo de pacote abre um novo thread imediatamente no horário definido (pode ser preciso até o segundo nível) e, em seguida, executa a tarefa especificada. O problema é que se o tempo de execução desta tarefa for muito longo, a próxima tarefa será executada. começará de novo. Haverá problemas. Embora possam ser resolvidos, eles são mais problemáticos.

Geralmente uso essas tarefas agendadas quando preciso gerar informações de log regularmente e realizar autocorreção e autoteste do programa, que são funções de baixa frequência que podem ser concluídas rapidamente. Essas funções apenas leem os dados no banco de dados e não há operações de gravação ou atualização, portanto, não custa nada deixar o apscheduler abrir outro thread para processamento.

No entanto, a lógica que exige uma consulta constante e frequente dos preços de mercado ou do estado da política local não é adequada para tal pacote de tempo e precisa de ser controlada por um loop while.

Por exemplo, se a estratégia utiliza sinais dentro da barra da linha K, isso significa que o sinal pode aparecer a qualquer momento, e você precisa verificar constantemente o preço subjacente ou o volume de negociação para determinar se as operações correspondentes precisam ser realizadas. Se precisar de um controle de tempo mais preciso neste momento, por exemplo, execute apenas a lógica série de abertura de uma posição 10 segundos antes do final da hora da linha k. Então, como você pode simplesmente julgar se são os últimos 10 segundos?

Peguei o restante de 3600 usando carimbo de data/hora.

(Se você não estiver familiarizado com o carimbo de data e hora no computador, ou seja, o carimbo de data e hora, ele começa no horário de Greenwich 1970.1.1. Por exemplo, é sobre o segundo 1691240298. Não há problema com a precisão do computador com relação ao nível de microssegundos.)

Depois de tirar o restante de 3.600, se o restante for maior que 3.590 segundos, serão os últimos dez segundos. O erro pode chegar ao nível de milissegundos. Como mencionado anteriormente, é melhor que as principais operações de transação sejam executadas sequencialmente em um thread. Dessa forma, o tempo de execução pode ser controlado aproximadamente ou o período de tempo pode ser confuso, o que não será problema.

Este tipo de controle exige que a configuração de horário do seu próprio servidor não se desvie muito do horário padrão. Porém, este tipo de controle não será muito preciso, podendo atingir apenas o segundo nível. No entanto, o Python não pode controlar o tempo com muita precisão e essas funções de suspensão causarão erros de cerca de microssegundos.

Reduza a frequência de acesso à API

Se você adotou um centro de mercado independente baseado em websocket para compartilhar informações públicas de mercado, como linhas K, entre diferentes estratégias ou até mesmo códigos de negociação diferentes, então não há preocupação com as limitações de frequência da API a esse respeito.

Mas também precisamos obter informações de contas em tempo hábil, ou seja, três tipos de informações: fundos, posições e pedidos. Embora as informações da conta também possam usar o websocket e esperar que o servidor Exchange as envie ativamente, é mais simples usar diretamente a API restante por três motivos.

Primeiro, para reduzir a carga sobre as informações da conta enviadas pelo websocket, a exchange exige que a exchange reconecte ou atualize o listenKey de vez em quando (Binance leva 60 minutos). Esta situação é obviamente mais difícil de manter. Embora não seja um grande problema, se você hospedar API e negociar várias contas ao mesmo tempo, será necessário ativar um programa separado para garantir a precisão das informações em tempo real, e a lógica se tornará muito mais complicada.

Em segundo lugar, algumas bolsas não possuem websocket push para atualizações de informações da conta. Nesse caso, a lógica de negociação não é universal e é mais problemático alternar códigos de negociação reais entre diferentes bolsas. É claro que a maioria das pessoas não negociará em múltiplas bolsas e não encontrará tal situação.

Terceiro, se o link do websocket estiver quebrado ou encontrar problemas como a atualização ou expiração do listenKey mencionado anteriormente, se o programa não descobrir isso a tempo e houver uma transação neste momento, então se você confiar apenas no websocket, isso causará um grande problema de comparação, porque as informações perdidas de atualização da conta não serão enviadas novamente. Para evitar a possibilidade de tais problemas potenciais, você também deve confiar na API restante para obter determinadas informações da conta. Então no final você tem que contar com a API restante, não é desnecessário?

Em resumo, para transações de média e baixa frequência, é melhor usar diretamente a API REST para obter informações da conta. Para fontes de dados importantes, é melhor seguir SSOT (Single Source Of Truth), caso contrário, pode causar confusão potencial. Este conceito pode ser pesquisado no Google por você mesmo e é muito útil em programação que depende de processamento de dados.

Então, se você usar apenas a API REST para obter informações da conta, poderá manter uma tabela temporária de informações da conta na memória do código local, especialmente os fundos disponíveis. Isso pode reduzir as visitas frequentes à bolsa. você pode enviar todos os pedidos de uma vez. Semelhante a fazer pedidos em lotes e, em seguida, processar lentamente a lógica por trás da atualização, quanto mais rápido o pedido for feito, menor será o desvio.

Além disso, para determinar se a ordem de stop-loss algorítmica foi preenchida, você pode usar os preços máximos e mínimos recentes em vez de obter diretamente o status da ordem pendente, porque para determinar se a ordem foi preenchida, você precisa pesquisar continuamente, o que é relativamente frequente e causará muitos acessos inválidos à API. A atualização do preço da linha K do websocket é muito oportuna e não ocupa o limite de frequência da API.

Da mesma forma, algumas ordens de saída também podem ser assim. Se o seu sinal de saída depende do preço dentro de uma barra, pode ser melhor usar preços máximos e mínimos recentes. Como às vezes o preço é acionado e retorna imediatamente, se a frequência de verificação não for muito alta, você poderá perdê-lo. Para garantir consistência em sua estratégia de negociação, verifique a máxima e a mínima para que os preços não sejam perdidos. Esta abordagem tem a vantagem adicional de o preço real da transação poder ser mais favorável do que o preço real de desencadeamento.

Não sei se os dois pontos acima são fáceis de entender. Pode exigir alguma experiência na vida real.

Gostaria de compartilhar um pouco da minha experiência no desenvolvimento de código real, esperando que seja útil para todos.