Experiência VonmoTrade. Parte 3: Livro de warrants. Processamento e armazenamento de informações comerciais


No último artigo do ciclo, nos familiarizamos com os tipos de ordens de troca. Hoje, analisaremos o livro de pedidos, o processamento de aplicativos e questões relacionadas à organização do armazenamento de informações comerciais.


Oferta e Demanda


Certamente você se lembra da lei da oferta e demanda do curso da economia, que mostra a mecânica do mercado para a formação de preços:



A mesma mecânica trabalha em trocas.


A carteira de pedidos é uma lista na qual são inseridos pedidos limitados de vendedores e compradores, mostrando assim o interesse atual em um instrumento financeiro específico.


Se você converter o gráfico anterior em relação à carteira de pedidos, obterá algo assim:



Aqui vemos que o preço de mercado é obtido quando o preço máximo de demanda e o preço mínimo de oferta são iguais. Spread é a diferença entre esses preços. Este é um indicador importante, pois está associado à liquidez do instrumento. Quanto menor a propagação, mais líquido o instrumento. Para garantir liquidez no âmbito do comércio de câmbio, eles geralmente impõem um limite ao spread máximo, acima do qual a negociação pode ser interrompida.


Criação de aplicações


Considere o ciclo de vida de um pedido, desde a admissão até a troca, até o preenchimento ou cancelamento. Por simplicidade, consideraremos o caso do mercado de câmbio. Um processo especial é responsável pela lógica do processamento de pedidos, vamos chamá-lo de controlador de mercado.


Assim, o participante cria um aplicativo e chega ao intercâmbio. O controlador deve garantir que o participante tenha liquidez suficiente para criar o tipo de pedido solicitado. A fonte de informações pode ser um serviço de contabilidade interna ou qualquer API externa.


Para a execução imediata desse aplicativo, um aplicativo chamado emparelhado deve existir no mercado.


Se houver uma contra ordem, do par encontrado, a ordem menor será executada na íntegra e a parte maior. Obviamente, se a execução parcial for permitida pelas instruções de negociação do aplicativo. Na ausência de uma contra-ordem, uma nova ordem entra no livro de ordens e ocupa seu lugar na lista de ordens do seu tipo.


Como apenas pedidos pendentes se enquadram no livro de pedidos, para pedidos de outros tipos, é necessário selecionar suas listas.


Em todas as listas de pedidos, o lado da compra deve ser classificado em ordem decrescente, e o lado da venda deve ser classificado em ordem crescente. O primeiro elemento da lista de ordens limitadas para cada lado forma o melhor preço de oferta e demanda, respectivamente.


Outro ponto importante é a ordem de execução. O controlador deve implementar FIFO. Portanto, se os preços dos dois lances coincidirem, o criado anteriormente deverá ser mais alto.


Na interface do usuário, o livro se parece com uma tabela que consiste em um conjunto de níveis de preços, que apresenta pedidos limitados para compra e venda.



Para distinção visual adicional, os pedidos de venda e compra têm cores diferentes.


Agregação de nível


Profundidade do livro - O número de níveis de preços. Para mercados ativos com um grande número de pedidos pendentes, separados um do outro por uma distância mínima, a profundidade pode ser muito grande para exibição no terminal do trader. Para avaliar o livro inteiro, você precisa de uma ferramenta de agrupamento de níveis.


Cortando uma casa decimal e agrupando os níveis, podemos reduzir seu número a cada etapa.


Execução e cancelamento de pedidos


Depois que a ordem no livro é executada ou cancelada, o controlador deve atualizá-lo, excluindo essa ordem e notificando todos os interessados ​​nas alterações no livro.


Arquitetura e dimensionamento do manipulador


Dado o desempenho e a confiabilidade necessários, é necessário determinar abordagens para dimensionar aplicativos e sistemas de armazenamento.


Normalmente, as trocas usam escala vertical. O código para processar aplicativos e contas de usuário é executado em uma máquina em um único monólito. Essa abordagem mostra bom desempenho, mas tem uma limitação significativa - em qualquer caso, a escala vertical tem um limite, tanto em termos de potência do processador quanto de capacidade de armazenamento.


Como parte do experimento, decidi que o processamento do mercado deveria ser escalado horizontalmente. Cada ferramenta individual é processada por seu próprio processo. Os processos são distribuídos entre os nós do cluster automaticamente. Em caso de falha, o mercado é transferido para outro nó sem perda de status.



A fórmula do sistema é extremamente simples: manipuladores M são distribuídos nos nós K do cluster e usam armazenamentos de dados L.
Um esquema semelhante permite dimensionar o sistema para cerca de 150 nós. E cada controlador de mercado pode lidar com cerca de 30k RPS.


Como o fluxo de aplicativos em todos os mercados é diferente e depende da atividade do usuário, os mercados podem ser divididos em vários grupos: pequeno, médio e grande porte. Cada nó possui configurações que permitem especificar limites no número de mercados que ele pode processar. O assistente distribui automática e uniformemente mercados do mesmo tipo entre nós do cluster. No caso de uma alteração na composição do cluster, os mercados são redistribuídos. Assim, é alcançada uma distribuição mais ou menos uniforme da carga no sistema.


Um exemplo de exibição de nós na interface de gerenciamento do Exchange:



Armazenamento de dados


A carteira de pedidos está em constante mudança e deve ser mantida na memória. Para o MVP, escolhi o Tarantool com o WAL como armazenamento em memória. Todos os dados históricos serão registrados no PostgreSQL.


O esquema para armazenar dados atuais e históricos deve corresponder ao esquema selecionado para dimensionar o código dos manipuladores. Cada mercado pode usar seus próprios postgres e tarantool. Para fazer isso, combine o par de postgresql e tarantool em uma única entidade - um data warehouse de mercado.


Ao configurar o mercado, o administrador pode gerenciar repositórios. Para manter a flexibilidade, em vez de detalhes de acesso para instâncias específicas do postgresql e tarantool, especificaremos um identificador exclusivo para o conjunto de conexões. A interface desses conjuntos é suportada pela plataforma. Assim, o repositório na interface de administração se parece com isso:



Ao configurar um mercado, o administrador deve especificar pelo menos uma loja para cada mercado. Se você especificar alguns, obterá um mercado com replicação lógica de dados. Esse recurso permite configurar a confiabilidade e o desempenho de um esquema de armazenamento.


Dados do livro de pedidos


Tarantool usa espaço para organizar os dados armazenados. A declaração dos espaços necessários para a carteira de pedidos é a seguinte:


book = { state = { name = 'book_state', id = 1, }, orders = { limit = { buy_orders = { name = 'limit_buy_orders', id = 10, }, sell_orders = { name = 'limit_sell_orders', id = 20, }, }, market = { buy_orders = { name = 'market_buy_orders', id = 30, }, sell_orders = { name = 'market_sell_orders', id = 40, }, }, ... }, orders_mapping = { name = 'orders_mapping', id = 50, }, } 

Como vários mercados podem armazenar seus dados em uma instância do tarantool, adicionaremos um identificador de mercado a todas as entidades. A implementação atual do livro baseia-se no princípio de contar uma vez, muitas vezes dando. Durante as operações de atualização de livros, os agrupamentos são recontados automaticamente. Por exemplo, adicionamos um pedido ao mercado, a precisão dos preços é 6, existem 6 grupos de preços possíveis + uma fatia com os dados originais do pedido que precisam ser atualizados.


Existem muitos pedidos de mapeamento de pedidos para emitir listas de pedidos de clientes ativos.


Graças ao modelo de dados tarantool, usando uma combinação de índices e vários iteradores de amostragem, o código lua que implementa o armazenamento da carteira de pedidos leva apenas 600 linhas (junto com a inicialização).


Dados históricos


Os dados do mercado são armazenados em tabelas separadas para cada mercado. Considere um conjunto de tabelas base.


Histórico de aplicativos concluídos


Para salvar os resultados dos aplicativos de processamento, use a tabela de histórico. Inclui aplicativos totalmente concluídos, bem como cancelados, mas parcialmente concluídos.


 CREATE TABLE public.history ( id uuid NOT NULL, ts timestamp without time zone NOT NULL DEFAULT now(), owner character varying(75) COLLATE pg_catalog."default" NOT NULL, order_type integer NOT NULL, order_side integer NOT NULL, price numeric(64,32) NOT NULL, qty numeric(64,32) NOT NULL, commission numeric(64,32) NOT NULL, opts jsonb NOT NULL, CONSTRAINT history_pkey PRIMARY KEY (id, ts) ) 

Por sua vez, a emissão para usuários finais é baseada no histórico de suas propostas.


Feed de Dados Históricos


Para fins de análise, bem como a formação de um feed de dados históricos, após cada transação, o controlador de mercado deve salvar informações sobre esse evento. Para corrigir eventos de mudanças no mercado, use a tabela de ticks:


 CREATE TABLE public.ticks ( ts timestamp without time zone NOT NULL, bid numeric(64,32) NOT NULL, ask numeric(64,32) NOT NULL, last numeric(64,32) NOT NULL, bid_vol numeric(64,32), ask_vol numeric(64,32), last_vol numeric(64,32), opts jsonb DEFAULT '{}'::jsonb, CONSTRAINT ticks_pk PRIMARY KEY (ts) ) 

Ele armazena preços e volumes de mercado após a transação, e o campo opts contém informações de serviço, como uma descrição dos pedidos envolvidos na transação.


Feed de dados do gráfico


Para criar gráficos de negociação, a tabela de ticks é suficiente. Ele contém o chamado fluxo bruto, mas o postgresql possui poderosas funções analíticas e permite agregar dados sob demanda.


Os problemas começam quando há muitos dados e já não há energia suficiente. Para resolver, crie uma tabela com dados pré-calculados:


 CREATE TABLE public.df ( t timestamp without time zone NOT NULL, r df_resolution NOT NULL DEFAULT '1m'::df_resolution, o numeric(64,32), h numeric(64,32), l numeric(64,32), c numeric(64,32), v numeric(64,32), CONSTRAINT df_pk PRIMARY KEY (t, r) ) 

Falaremos sobre como trabalhar com séries temporais no Postgresql, preparar dados para a tabela df e como criar gráficos no próximo artigo.


Sumário


Descobrimos os principais pontos de organização do livro de pedidos e o mecanismo de processamento de pedidos, bem como um pouco imerso na prática de trabalhar com dados de mercado.


O esquema de armazenamento selecionado permite que você comece de uma loja para todos os mercados e, à medida que o projeto cresce, distribua os mercados em lojas diferentes, colocando-os o mais próximo possível dos processadores do mercado.

Source: https://habr.com/ru/post/pt482254/


All Articles