Experiência VonmoTrade. Parte 4: Gráficos de negociação


Em artigos anteriores, descobrimos como as ordens comerciais são criadas e processadas. O tópico deste artigo serão as questões de processamento e armazenamento de informações necessárias para ferramentas gráficas de análise de mercado - gráficos de ações.


Antes de começar, quero fazer uma pequena digressão. Para projetos internos do Vonmo, é usado o esquema usual de nomeação de palavras V +, que descreve de maneira mais sucinta as funções do projeto. Hoje descobri que a VTrade é uma empresa existente. Para evitar confusão, renomei o experimento para VonmoTrade.


Para avaliar o estado do mercado, um livro de pedidos e histórico de transações não é suficiente. Precisamos de uma ferramenta que nos permita identificar clara e rapidamente a tendência do preço de mercado. Os gráficos de negociação podem ser divididos em dois tipos:


  1. Linear;
  2. Intervalo

Gráficos de linha


A programação mais simples e compreensível, sem preparação. Exibe a dependência do preço de um instrumento financeiro no prazo.



A principal vantagem desse tipo de gráfico é a simplicidade. A principal desvantagem é consequência disso - baixo conteúdo de informação.


Se o gráfico for baseado em dados brutos, o preço do último fechamento será obtido. Mas geralmente, os gráficos são construídos com base em dados agregados. Nesse caso, o preço de fechamento de cada intervalo é obtido. Como descartamos tudo o que aconteceu no intervalo e assumimos apenas o preço de fechamento do intervalo, por isso, o conteúdo da informação é perdido.


Resolução gráfica


Se começarmos a construir um gráfico com base em todas as mudanças de preço, ou seja, cada transação fechada cairá no gráfico, será difícil para uma pessoa percebê-lo. E a energia gasta no processamento e entrega de tal cronograma será gasta ineficientemente.


Portanto, os dados são reduzidos, dividindo o eixo do tempo em intervalos e agregando preços nesses intervalos.
Resolução - o tamanho do intervalo elementar da divisão do eixo do tempo: segundo, minuto, hora, dia e assim por diante.


Bares


Relacione-se a gráficos de intervalo. Para aumentar o conteúdo das informações, é necessário que cada intervalo de tempo exiba informações de preços no início e no final do intervalo, bem como o preço máximo e mínimo. A exibição gráfica deste conjunto é chamada de barra. Considere o esquema de uma barra:



A sequência de barras forma um gráfico:



Velas japonesas


Como barras, consulte gráficos de intervalo. Eles são o tipo mais popular de gráfico na análise técnica. Uma vela consiste em um corpo preto ou branco e sombras: superior e inferior. Às vezes, uma sombra é chamada de pavio. A borda superior e inferior da sombra exibe os preços máximo e mínimo para o período correspondente. Os limites do corpo exibem os preços de abertura e fechamento. Vamos desenhar uma vela:



A sequência de velas forma um gráfico:



Notação OHLCV


No último artigo, descobrimos o esquema de armazenamento de dados para o gráfico no postgresql e criamos uma tabela para a fonte de dados que armazenará os dados agregados:


CREATE TABLE 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) ) 

Os campos não exigem explicação, exceto o campo r é a resolução da série. Existem enumerações no postgresql, é conveniente usá-las quando um conjunto de valores para um campo é conhecido antecipadamente. Através das enumerações, definimos um novo tipo para as resoluções de gráfico permitidas. Que seja uma série de um minuto a um mês:


 CREATE TYPE df_resolution AS ENUM ('1m', '3m', '5m', '15m', '30m', '45m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M'); 

É importante encontrar um equilíbrio entre o desempenho do sistema de disco, processador e custo total de propriedade. O sistema define atualmente 16 resoluções. Duas soluções são óbvias:


  • Podemos contar e armazenar todas as resoluções no banco de dados. A opção é conveniente, pois na amostragem não gastamos energia na agregação de intervalos, todos os dados estão imediatamente prontos para a saída. Em um mês, serão criados pouco mais de 72 mil registros para um instrumento. Parece simples e conveniente, mas essa tabela será alterada com muita frequência, pois para cada atualização de preço, é necessário criar ou atualizar 16 entradas na tabela e reconstruir o índice. No postgresql, também pode haver um problema com a coleta de lixo.
  • Outra opção é armazenar uma única resolução básica. Ao selecionar uma resolução básica, as resoluções necessárias devem ser construídas. Por exemplo, ao armazenar a resolução em minutos como base por mês, serão criados 43 mil registros para cada instrumento. Assim, comparado com a versão anterior, o volume de gravação e sobrecarga é reduzido em 40%. A carga do processador, no entanto, está aumentando.

Como mencionado acima, é importante encontrar um equilíbrio. Portanto, uma opção de compromisso não é armazenar uma resolução básica, mas várias: 1 minuto, 1 hora, 1 dia. Com esse esquema, 44,6 mil registros serão criados para cada instrumento por mês. A otimização do volume de gravação será de 36%, mas a carga no processador será aceitável. Por exemplo, para criar intervalos semanais em vez de ler e agregar 10.080 registros no caso de uma resolução básica de um minuto, precisamos ler do disco e agregar os dados de apenas 7 dias de resolução.


Armazenamento OHLCV


Por natureza, o OHLCV é uma série temporal. Como você sabe, um banco de dados relacional não é muito adequado para armazenar e processar esses dados. Para resolver esses problemas, o projeto usa a extensão Timescale .


A escala de tempo melhora o desempenho das operações de inserção e atualização, permite configurar o particionamento e fornece funções analíticas otimizadas especificamente para trabalhar com séries temporais.


Para criar e atualizar barras, precisamos apenas de funções padrão:


  • date_trunc('minute' | 'hour' | 'day', transaction_ts) - para encontrar o início do intervalo da resolução de minutos, horas e dias, respectivamente.
  • greatest e least para determinar os preços máximo e mínimo.

Graças à API upsert, apenas uma solicitação de atualização é executada por transação.
Eu tenho esse tipo de SQL para corrigir alterações de mercado em resoluções básicas:


 FOR i IN 1 .. array_upper(storage_resolutions, 1) LOOP resolution = storage_resolutions[i]; IF resolution = '1m' THEN SELECT DATE_TRUNC('minute', ts) INTO bar_start; ELSIF resolution = '1h' THEN SELECT DATE_TRUNC('hour', ts) INTO bar_start; ELSIF resolution = '1d' THEN SELECT DATE_TRUNC('day', ts) INTO bar_start; END IF; EXECUTE format( 'INSERT INTO %I (t,r,o,h,l,c,v) VALUES (%L,%L,%L::numeric,%L::numeric,%L::numeric,%L::numeric,%L::numeric) ON CONFLICT (t,r) DO UPDATE SET h = GREATEST(%Ih, %L::numeric), l = LEAST(%Il, %L::numeric), c = %L::numeric, v = %Iv + %L::numeric;', df_table, bar_start, resolution, price, price, price, price, volume, df_table, price, df_table, price, price, df_table, volume ); END LOOP; 

Na amostragem, para a agregação de intervalos, precisamos das seguintes funções:


  • time_bucket - para quebrar em intervalos
  • first - para encontrar o preço de abertura - O
  • max - o preço mais alto para o intervalo - H
  • min - o menor preço por intervalo - L
  • last - para encontrar o preço de fechamento - C
  • sum - para encontrar o volume de negociação - V

O único problema encontrado ao usar a escala de tempo são as limitações da função time_bucket . Permite operar apenas em intervalos inferiores a um mês. Para criar uma resolução mensal, você deve usar a função date_trunc padrão.


API


Para exibir gráficos no cliente, usaremos gráficos leves do Tradingview. A biblioteca permite que você personalize totalmente a aparência dos gráficos e é conveniente de usar. Eu tenho os seguintes gráficos:



Como a parte principal da interação entre o navegador e a plataforma é pelo websocket, não há problemas com a interatividade.


Fonte de dados


A fonte de dados para os gráficos (feed de dados) deve retornar a parte necessária da série temporal na resolução necessária. Ao mesmo tempo, para economizar tráfego e reduzir o tempo de processamento no cliente, o servidor deve compactar os pontos.


A API do feed de dados inicialmente precisa ser projetada para que você possa solicitar vários gráficos em uma solicitação e se inscrever nas atualizações. Isso reduzirá o número de comandos e respostas no canal.


Considere um exemplo da solicitação dos últimos 50 minutos para USDGBP, com uma assinatura automática para atualizações de gráficos:


 { "m":"market", "c":"get_chart", "v":{ "charts":[ { "ticker":"USDGBP", "resolution":"1h", "from":0, "cnt":50, "send_updates":true } ] } } 

Obviamente, é possível solicitar um intervalo de datas (de, para), mas como o intervalo de cada barra é conhecido, a API declarativa que indica o momento e o número de barras me parece mais conveniente.
O feed de dados para esta solicitação responderá de maneira semelhante:


 { "m":"market", "c":"chart", "v":{ "bar_fields":[ "t","uts","o","h","l","c","v" ], "items":[ { "ticker":"USDGBP", "resolution":"1h", "bars":[ [ "2019-12-13 13:00:00",1576242000,"0.75236800", "0.76926400","0.75236800","0.76926400","138.10000000" ], .... ] } ] } } 

O campo bar_fields contém informações sobre as posições dos elementos. Uma otimização adicional é colocar esse campo na configuração do cliente que ele recebe do servidor no momento da inicialização.


Assim, o cliente recebe a parte necessária dos dados históricos e constrói o estado inicial do gráfico. Se o estado mudar, ele receberá uma atualização que afeta apenas a última barra.


 { "m":"market", "c":"chart_tick", "v":{ "ticker":"USDGBP", "resolution":"1h", "items":{ "v":"140.600", "ut":1576242000, "t":"2019-12-13T13:00:00", "o":"0.752368", "l":"0.752368", "h":"0.770531", "c":"0.770531" } } } 

Resultado preliminar


Ao longo da série de artigos, analisamos a teoria e a prática da construção de uma troca. É hora de montar o sistema.


No próximo artigo, abordaremos o desenvolvimento de interfaces gráficas de usuário: UI de serviço para gerenciamento de plataforma e UI para usuários finais. Uma versão demo do Vonmo Trade também será apresentada.

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


All Articles