Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

Программирование

Os inspiradores por trás do desenvolvimento do serviço de estatísticas para a Tinkoff Investments foram:

O que será discutido?

  • Apenas a parte aplicada sobre o desenvolvimento.
  • Conhecimento e experiência reais, que são muito importantes no trabalho com instrumentos financeiros.
  • Visão geral dos problemas a serem trabalhados

Portanto, quero calcular as estatísticas comerciais e fazê-lo de maneira conveniente. 

Desenvolvendo um serviço de estatísticas passo a passo: 

  1. Conexão com a API Tinkoff Invest
  2. Desenhando dados da Tinkoff Invest API em um navegador
  3. Recebimento de relatórios e transações da corretora
  4. Cálculo e saída de informações de interesse
  5. Conclusões e planos para o futuro

Conexão com a API Tinkoff Invest

Para se conectar à API, você pode obter qualquer SDK da documentação https://github.com/Tinkoff/investAPI#sdk . Ou pacote npm ` tinkoff-sdk-grpc-js `. É importante que o pacote seja atualizado para a versão mais recente pelos desenvolvedores. Instalar

npm i tinkoff-sdk-grpc-js

verificando

const { createSdk } = require(‘tinkoff-sdk-grpc-js’);   // Token que pode ser obtido assim  const TOKEN = ‘SUAAPI’;   // O nome do aplicativo pelo qual você pode ser encontrado nos logs do TCS. const appName = ‘tcsstat’;   const sdk = createSdk(TOKEN, appName); (async () => {     console.log(await sdk.users.getAccounts()); })();

Resultado: uma lista de suas contas será exibida no console. Por exemplo, vamos analisar as nuances:Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

  • Na lista de contas existe um “Banco de investimento”, com o qual você não pode trabalhar usando a API
  • Observe que os campos vêm em camelCase, enquanto na documentação esses campos são apresentados em under_score. 
  • Será assim em todos os lugares, então você não pode simplesmente pegar e copiar um campo da documentação.

Útil:

  • Você pode encontrar este código na ramificação do projeto

https://github.com/pskucherov/tcsstat/tree/step1 https://github.com/pskucherov/tcsstat/compare/step1   

Desenhando dados da Tinkoff Invest API em um navegador

Peguei next.js e socket.io. Esta não é uma recomendação forte, escolha a seu critério. 

npx create-next-app@latest npm i socket.io socket.io-client

Prosseguimos imediatamente para a etapa de amizade next+socket+investapi e consulte a seção Útil desta etapa para obter todos os detalhes.  Vou descrever os detalhes: 

  • No lado do nodejs (servidor), existe um arquivo pages/api/investapi.js. É aqui que criamos o servidor socket.io e nos conectamos ao investapi.
  • Do lado do navegador (cliente), nos conectamos ao servidor por meio de um soquete e solicitamos os dados da conta do corretor. 
  • Recebemos os dados do corretor no servidor e os enviamos ao cliente. Ao serem recebidos no cliente, são exibidos no navegador. 

Resultado:  no console do navegador podemos ver informações sobre as contas. Ou seja, na última etapa vimos as informações das contas no console do servidor (nodejs), na etapa atual transferimos essas informações para o cliente (navegador).

Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

Agora vamos fazer com que você possa selecionar uma conta no navegador e, se não houver token, um erro será enviado ao console. O trabalho é simples e nada de novo, então dou apenas links para commits

  1. https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
  2. https://github.com/pskucherov/tcsstat/commit/b28ac973a57494f5232589b4cb6b9fb13b8af759 

Útil:

  • Como fazer amigos em seguida e soquete é descrito em detalhes aqui
  • Código de amizade next+socket+investapi:

https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 Para quem o acima é difícil, então permanecemos nesta fase e lidamos com o código. Se você possui dúvidas, pergunte. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2

Recebimento de relatórios e transações da corretora

Existem três métodos para receber relatórios e transações da corretora

  1. GetBrokerReport
  2. GetDividendsForeignIssuer
  3. GetOperationsByCursor

Desde o início é importante saber: 

  • O relatório da corretora é gerado no modo T-3, ou seja, as negociações são exibidas lá após sua execução real. 
  • Assim, se você solicitar este relatório para os últimos dois dias, ele estará pronto em três dias. 
  • Para obter as transações dos últimos dias, utilizamos o método de recebimento de transações, mas lembre-se que seu id e conteúdo podem mudar após a geração do relatório da corretora.

GetBrokerReport

Para obter um relatório de corretagem, você precisa levar o id da conta, data de início e data final do relatório, mas não mais que 31 dias. Enviamos uma solicitação para gerar um relatório para a API em generate _broker_report_request , obtemos um taskId em resposta. Depois disso, usando esse taskId, obtemos dados de get _broker_report_response.

Assim diz a documentação, na realidade existem nuances. Cuidado com as mãos:
  • Você precisa salvar o TaskID para sempre exatamente para essas datas. 
  • Já que se você o perder, para as datas solicitadas, o relatório virá primeiro em resposta à solicitação de geração, 
  • E então não virá de jeito nenhum.
Vamos começar a escrever o código

Método para obter a data, tendo em conta a subtração da data atual

const getDateSubDay = (subDay = 5, start = true) => {     const date = new Date();     date.setUTCDate(date.getUTCDate() – subDia);       if (início) {         data.setUTCHours(0, 0, 0, 0);     } else {         data.setUTCHours(23, 59, 59, 999);     }       data de retorno; };

Solicitação de geração de relatório 

const brokerReport = await (sdk.operations.getBrokerReport)({         generateBrokerReportRequest: {             accountId,             de,             para,         }, });

Resultado:

  • Como resultado da primeira execução do comando, obtemos o taskId. 
  • O relatório começa a ser gerado do lado do corretor. Quando estiver pronto é desconhecido, esperamos e puxamos periodicamente o taskId em antecipação ao relatório.
  • Por que? Porque se o relatório não estiver pronto, ele gera um erro. Se o relatório não estiver pronto do lado do corretor, isso é um erro no seu código. Processe: 30058|INVALID_ARGUMENT|tarefa ainda não concluída, tente novamente mais tarde

O código para aguardar e receber um relatório é mais ou menos assim.

const timer = tempo assíncrono => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getBrokerResponseByTaskId = async (taskId, page = 0) => {     try {         return await (sdk.operations.getBrokerReport)({             getBrokerReportRequest: {                 taskId,                 page,             },         });     } catch (e) {         console.log(‘wait’, e);         espera timer(10000);         return await getBrokerResponseByTaskId(taskId, página);     } };

Então a mesma mágica acontece. Paramos nosso script, iniciamos novamente, não temos um taskId. Executamos o código com a requisição taskId, mas não obtemos mais o taskId, mas imediatamente o relatório. Magia! E tudo estaria bem se fosse sempre assim. Mas em um mês não haverá dados. Útil :

  • Um pouco de teoria é delineada aqui e aqui .
  • Juntando o código, o rascunho ficará mais ou menos assim.

https://github.com/pskucherov/tcsstat/tree/step3.1 https://github.com/pskucherov/tcsstat/compare/step3.1

  • Se alguém se deparar com isso, seja bem-vindo ao problema . Depois que eles consertarem essa magia, ela perderá seu poder e ficará diferente de alguma forma. Mas no momento atual (21/03/2023) funciona assim mesmo.

GetDividendsForeignIssuer

Alguém pode pensar que o método é semelhante ao anterior e você pode usar um único método em que apenas altera o nome das operações. Mas eles não adivinharam!  A nomenclatura é muito diferente tanto nos métodos quanto nas informações retornadas. E a contagem de páginas começa de 0, depois de 1. Para não se confundir com tudo isso, é mais fácil escrever dois métodos diferentes. O que é estranho, porque a lógica do trabalho é a mesma. Cuspi por muito tempo quando tentei criar um método e havia menos código. Não haverá exemplos aqui.

GetOperationsByCursor

Meu favorito dos três. Embora não seja o mais preciso, mas o mais adequado. Fazemos uma solicitação desde o início da criação de uma conta até a data máxima possível (fechamento de conta ou atual). Obtemos a resposta, pegamos o cursor e solicitamos novamente, desde que haja dados.  E o código é mais conciso do que nos exemplos acima.

const timer = tempo assíncrono => {     return new Promise(resolve => setTimeout(resolve, time)); }   const getOperationsByCursor = async (sdk, accountId, from, to, cursor = ”) => {     try {         const reqData = {             accountId,             from,             to,             limit: 1000,             state: sdk.OperationState.OPERATION_STATE_EXECUTED,             withoutCommissions: false,             semNegociações: false,             semPernoites: false,             cursor,         };           return await sdk.operations.getOperationsByCursor(reqData);     } catch (e) {         await timer(60000);         return await getOperationsByCursor(sdk, accountId, from, to, cursor = ”);     } };

O rascunho a ser executado está aqui: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 Agora estamos prontos para adicionar operações de recebimento a nosso aplicativo. Se feito corretamente, você precisará obter relatórios de corretagem para toda a existência da conta. E para os dados perdidos, esses mesmos T-3s, recarregam das operações. Mas isso pode ser separado em um artigo separado. Uma das principais nuances que você encontrará é colar operações e um relatório de corretagem.

  •  Se hoje você recebeu um relatório de corretagem e transações para as datas solicitadas, coloque tudo no banco de dados, então não há problemas. 
  • Você terá problemas amanhã quando receber a próxima parte dos dados do relatório e das operações e decidir sincronizá-los com o banco de dados existente. 
  • Muitas nuances sobre incompatibilidade ou alteração de id após o processamento
  • Então, para o mercado OTC, os id não combinam.
  •  Assim como as nuances de sincronização dos instrumentos, que novamente não coincidem, devido às peculiaridades da API. Mas isso é outra história.

Vamos adicionar obter informações sobre operações ao nosso aplicativo. A questão principal será onde os dados serão processados ​​e armazenados.

  •  Se você fizer isso sozinho, consumirá os mesmos dados de diferentes dispositivos. Então você precisa processar e armazenar dados no servidor.
  • Se você tiver muitos dados diferentes consumidos por muitos usuários diferentes, precisará decidir o que é mais importante: a velocidade dos usuários ou a economia de ferro do seu lado. Quem pode pagar uma quantidade infinita de hardware conta tudo em seu servidor e torna super rápido para os usuários, economizando recursos do usuário, como bateria e tráfego, o que é muito importante nos telefones.

Por sua vez, contar no navegador não é a solução ideal em princípio. Portanto, o que não é caro, consideramos em nosso servidor. Deixamos o resto para o cliente. Eu realmente quero pegar e calcular a comissão no servidor. Mas aí entra a nuance chamada “interatividade”. Digamos que você tenha milhares de operações e leve cinco minutos para recebê-las. O que o usuário terá neste momento? Girador? Progresso? Infa sobre quanto foi carregado? É ideal usar “espera ativa” quando o usuário no processo já pode ver algo. Aqui está o Resultado:Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

  • Carregamento da página
  • Todas as faturas são solicitadas
  • Depois disso, todas as transações com comissões por transações executadas são solicitadas para todas as contas. À medida que os dados são recebidos, eles são renderizados no navegador.

Para não filtrar os dados nos eventos toda vez, extraímos nosso próprio evento para cada conta. Assim:

socket.emit(‘sdk:getOperationsCommissionResult_’ + accountId, {                 items: data?.items,                 inProgress: Boolean(nextCursor), });

O rascunho para lançar está aqui: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 Seguindo em frente. É ótimo que você tenha lido esta linha! 

Cálculo e saída de informações de interesse

Depende de quem precisa de qual informação. Portanto, digo imediatamente as principais nuances que você encontrará.

Trabalhando com preços 

Todo mundo que trabalha com finanças sabe que transações com dinheiro só devem ser feitas com números inteiros. Devido à imprecisão dos valores após o ponto decimal e ao erro cumulativo com um grande número de operações. É por isso que todos os preços são apresentados no seguinte formato MoneyValueEstamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

campotipoDescrição
moedacordaString código de moeda ISO
unidadesint64Parte inteira da soma, pode ser um número negativo
nanoint32Parte fracionária do valor, pode ser um número negativo

Nós os processamos separadamente e depois os trazemos para o valor do preço:

cotação.unidades + cotação.nano / 1e9

O custo dos contratos futuros

O preço dos futuros é apresentado em pontos, quando você tem um futuro de moeda, você precisa saber a taxa. E, claro, o preço em pontos e o escalão de preços. Quando você calcula o lucro das transações, isso pode disparar, porque. se você calcular o valor total multiplicando o preço pela quantidade. Aqui você precisa ter cuidado. Por enquanto, veremos como será. Isso se aplica a futuros de moeda, em outros lugares está tudo bem com isso.Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

mercado de balcão

Este mercado tem muitas peculiaridades, então vamos estudar as operações nele separadamente.Ao iniciar a sincronização das operações, você precisará trazer o figi / ticker para o mesmo formulário para corresponder corretamente ao instrumento. Quando você começar a sincronizar isso com o relatório da corretora, descobrirá que o tradeID da mesma transação possui letras no início das transações e elas não estão no relatório da corretora. Portanto, eles não podem ser comparados … ahem-ahem … por comparação! Eu combinei o horário da negociação, o ticker e a correspondência de que um tradeId está contido em outro. Certo, não sei. Quem se depara com isso e se preocupa com isso, aborda o problema ou começa um novo.Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

Operações matemáticas em ferramentas

É impossível, sem olhar, realizar operações matemáticas com a lista inteira. Para não adicionar quente a macio, sempre verificamos a moeda e processamos apenas se tivermos certeza de que a moeda corresponde e os pontos são convertidos na moeda desejada. Munidos do conhecimento sobre como trabalhar com números bancários, calcularemos a comissão gasta em cada uma das contas. Assim: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.   

O microsserviço está pronto!

https://github.com/pskucherov/tcsstat Como lição de casa, você pode verificar se o serviço funciona com uma conexão lenta, quando as conexões são interrompidas, quando a Internet é desconectada, quando há erros ou limites expirados por parte do corretor. 

Conclusões e planos para o futuro

  • Aprendeu sobre operações básicas e como trabalhar com a API Invest
  • Tempo gasto ~ 10 horas
  • Nível de dificuldade ~ júnior+ / médio baixo 

Se você continuar a refinar o microsserviço, poderá acabar com algo assim

https://opexbot.info

  Esse é o meu desenvolvimento, pra quem tem preguiça de entender, correr e contar por conta própria. Pretendo adicionar análises lá a pedido dos usuários. Se você gostou do artigo, inscreva-se no meu canal de telegrama . Estamos desenvolvendo um microsserviço usando a API Tinkoff Invest para automatizar o trabalho com relatórios de corretoras e cálculo de comissões.

Pavel
Rate author
Add a comment

  1. Isakiiev

    Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.

    Responder