Experimento VonmoTrade. Parte 4: Gráficos comerciales


En artículos anteriores, descubrimos cómo se crean y procesan las órdenes comerciales. El tema de este artículo será el procesamiento y almacenamiento de la información necesaria para las herramientas gráficas de análisis de mercado: gráficos de acciones.


Antes de comenzar, quiero hacer una pequeña digresión. Para los proyectos internos de Vonmo, se utiliza el esquema habitual de denominación de palabras V +, que caracteriza de manera más sucinta las funciones del proyecto. Hoy descubrí que VTrade es una empresa existente. Para evitar confusiones, cambié el nombre del experimento a VonmoTrade.


Para evaluar el estado del mercado, un libro de pedidos e historial de transacciones no es suficiente. Necesitamos una herramienta que nos permita identificar clara y rápidamente la tendencia del precio de mercado. Los gráficos comerciales se pueden dividir en dos tipos:


  1. Lineal;
  2. Intervalo

Gráficos lineales


El horario más simple y comprensible sin preparación. Muestra la dependencia del precio de un instrumento financiero a tiempo.



La principal ventaja de este tipo de gráfico es la simplicidad. El principal inconveniente se deriva de esto: bajo contenido de información.


Si el gráfico se basa en datos sin procesar, se toma el precio del último cierre. Pero generalmente, los gráficos se construyen sobre la base de datos agregados. En este caso, se toma el precio de cierre de cada intervalo. Dado que descartamos todo lo que sucedió en el intervalo y tomamos solo el precio de cierre del intervalo, debido a esto, el contenido de información se pierde.


Resolución de gráficos


Si comenzamos a construir un gráfico sobre la base de todos los cambios de precios, es decir, cada transacción cerrada caerá en el gráfico, entonces será difícil para una persona percibirlo. Y el poder gastado en el procesamiento y la entrega de dicho horario se gastará de manera ineficiente.


Por lo tanto, los datos se reducen dividiendo el eje de tiempo en intervalos y agregando precios en estos intervalos.
Resolución: el tamaño del intervalo elemental de división del eje de tiempo: segundo, minuto, hora, día, etc.


Bares


Relacionarse con gráficos de intervalos. Para aumentar el contenido de la información, es necesario que cada intervalo de tiempo muestre información de precios al principio y al final del intervalo, así como el precio máximo y mínimo. La visualización gráfica de este conjunto se llama barra. Considere el esquema de una barra:



La secuencia de barras forma un gráfico:



Velas japonesas


Al igual que las barras, consulte los gráficos de intervalos. Son el tipo de gráfico más popular en el análisis técnico. Una vela consiste en un cuerpo negro o blanco y sombras: superior e inferior. A veces una sombra se llama mecha. El borde superior e inferior de la sombra muestra los precios máximos y mínimos para el período correspondiente. Los límites del cuerpo muestran los precios de apertura y cierre. Dibujemos una vela:



La secuencia de velas forma un gráfico:



Notación OHLCV


En el último artículo, descubrimos el esquema de almacenamiento de datos para el gráfico en postgresql y creamos una tabla para la fuente de datos que almacenará los datos 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) ) 

Los campos no requieren explicación, excepto que el campo r es la resolución de la serie. Hay enumeraciones en postgresql, que son convenientes de usar cuando se conoce de antemano un conjunto de valores para un campo. A través de las enumeraciones, definimos un nuevo tipo para las resoluciones de gráficos permitidas. Que sea una serie de un minuto a un mes:


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

Es importante lograr un equilibrio entre el rendimiento del sistema de disco, el procesador y el costo total de propiedad. El sistema actualmente define 16 resoluciones. Dos soluciones son obvias:


  • Podemos contar y almacenar todas las resoluciones en la base de datos. La opción es conveniente porque al muestrear no gastamos energía en la agregación de intervalos, todos los datos están listos inmediatamente para la salida. En un mes, se crearán un poco más de 72 mil registros para un instrumento. Parece simple y conveniente, sin embargo, dicha tabla cambiará con demasiada frecuencia, ya que para cada actualización de precios es necesario crear o actualizar 16 entradas en la tabla y reconstruir el índice. En postgresql, adicionalmente puede haber un problema con la recolección de basura.
  • Otra opción es almacenar una única resolución básica. Al seleccionar una resolución básica, se deben construir las resoluciones requeridas. Por ejemplo, al almacenar la resolución de minutos como la base por mes, se crearán 43 mil registros para cada instrumento. Por lo tanto, en comparación con la versión anterior, el volumen de grabación y sobrecarga se reduce en un 40%. La carga del procesador, sin embargo, está aumentando.

Como se mencionó anteriormente, es importante encontrar un equilibrio. Por lo tanto, una opción de compromiso no es almacenar una resolución básica, sino varias: 1 minuto, 1 hora, 1 día. Con este esquema, se crearán 44,6 mil registros por cada instrumento por mes. La optimización del volumen de grabación será del 36%, pero la carga en el procesador será aceptable. Por ejemplo, para construir intervalos semanales en lugar de leer y agregar 10,080 registros en el caso de una resolución básica de un minuto, necesitamos leer desde el disco y agregar los datos de resoluciones de solo 7 días.


Almacenamiento OHLCV


Por naturaleza, OHLCV es una serie temporal. Como sabe, una base de datos relacional no es muy adecuada para almacenar y procesar dichos datos. Para resolver estos problemas, el proyecto utiliza la extensión Timescale .


La escala de tiempo mejora el rendimiento de las operaciones de inserción y actualización, le permite configurar la partición y proporciona funciones analíticas optimizadas específicamente para trabajar con series de tiempo.


Para crear y actualizar barras, solo necesitamos funciones estándar:


  • date_trunc('minute' | 'hour' | 'day', transaction_ts) : para encontrar el comienzo del intervalo de la resolución de minuto, hora y día, respectivamente.
  • greatest y least para determinar los precios máximos y mínimos.

Gracias a la API upsert, solo se ejecuta una solicitud de actualización por transacción.
Obtuve este tipo de SQL para arreglar los cambios del mercado en resoluciones 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; 

Al muestrear, para la agregación de intervalos, necesitamos las siguientes funciones:


  • time_bucket - para dividir en intervalos
  • first - para encontrar el precio de apertura - O
  • max - el precio más alto para el intervalo - H
  • min - el precio más bajo por intervalo - L
  • last - para encontrar el precio de cierre - C
  • sum - para encontrar el volumen de negociación - V

El único problema encontrado al usar Timescale son las limitaciones de la función time_bucket . Le permite operar solo a intervalos de menos de un mes. Para crear una resolución mensual, debe usar la función estándar date_trunc .


API


Para mostrar gráficos en el cliente, utilizaremos gráficos ligeros de Tradingview. La biblioteca le permite personalizar completamente la apariencia de los gráficos y es conveniente de usar. Tengo los siguientes gráficos:



Dado que la parte principal de la interacción entre el navegador y la plataforma es a través de websocket, no hay problemas con la interactividad.


Fuente de datos


La fuente de datos para los gráficos (alimentación de datos) debe devolver la parte necesaria de la serie de tiempo en la resolución requerida. Al mismo tiempo, para ahorrar tráfico y reducir el tiempo de procesamiento en el cliente, el servidor debe empacar los puntos.


La API de alimentación de datos inicialmente debe diseñarse para que pueda solicitar varios gráficos en una sola solicitud y suscribirse a sus actualizaciones. Esto reducirá la cantidad de comandos y respuestas en el canal.


Considere un ejemplo de la solicitud de los últimos 50 minutos para USDGBP, con una suscripción automática para actualizaciones de gráficos:


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

Por supuesto, puede solicitar un rango de fechas (de, a), pero como se conoce el intervalo de cada barra, la API declarativa que indica el momento y el número de barras me parece más conveniente.
El feed de datos para esta solicitud responderá de manera similar:


 { "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" ], .... ] } ] } } 

El campo bar_fields contiene información sobre las posiciones de los elementos. Una optimización adicional es colocar este campo en la configuración del cliente que recibe del servidor en el momento del arranque.


Por lo tanto, el cliente recibe la parte necesaria de los datos históricos y construye el estado inicial del gráfico. Si el estado cambia, recibe una actualización que afecta solo a la ú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


A lo largo de la serie de artículos, hemos estado analizando la teoría y la práctica de construir un intercambio. Es hora de armar el sistema.


En el próximo artículo, abordaremos el desarrollo de interfaces gráficas de usuario: interfaz de usuario de servicio para la gestión de la plataforma e interfaz de usuario para usuarios finales. También se presentará una versión demo de Vonmo Trade.

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


All Articles