Experimento VonmoTrade. Parte 3: Libro de warrants. Procesamiento y almacenamiento de información comercial.


En el último artículo del ciclo, nos familiarizamos con los tipos de órdenes de cambio. Hoy analizaremos el libro de pedidos, el procesamiento de las solicitudes y los problemas relacionados con la organización del almacenamiento de información comercial.


Oferta y demanda


Seguramente recuerdas la ley de oferta y demanda del curso de la economía, que muestra la mecánica del mercado para la formación de precios:



La misma mecánica trabaja en los intercambios.


El libro de pedidos es una lista en la que se ingresan los pedidos limitados de vendedores y compradores, mostrando así el interés actual en un instrumento financiero en particular.


Si convierte el gráfico anterior en relación con el libro de pedidos, obtendrá algo como esto:



Aquí vemos que el precio de mercado se obtiene cuando el precio máximo de demanda y el precio mínimo de oferta son iguales. Spread es la diferencia en estos precios. Este es un indicador importante, ya que está asociado con la liquidez del instrumento. Cuanto más pequeña es la extensión, más líquido es el instrumento. Para garantizar la liquidez en el marco de la negociación en bolsa, a menudo imponen un límite en el margen máximo, por encima del cual se puede detener la negociación.


Creación de aplicaciones.


Considere el curso de la vida de una solicitud desde la admisión al intercambio hasta el cumplimiento o la cancelación. Para simplificar, consideraremos el caso del mercado de divisas. Un proceso especial es responsable de la lógica del procesamiento de las órdenes, llamémoslo el controlador del mercado.


Entonces, el participante crea una aplicación, llega al intercambio. El controlador debe asegurarse de que el participante tenga suficiente liquidez para crear el tipo de pedido solicitado. La fuente de información puede ser un servicio de contabilidad interna o cualquier API externa.


Para la ejecución inmediata de esta aplicación, debe existir una llamada aplicación emparejada en el mercado.


Si hay una orden contraria, del par encontrado, la orden más pequeña se ejecuta en su totalidad y la más grande en parte. Por supuesto, si las instrucciones comerciales de la aplicación permiten la ejecución parcial. En ausencia de un contra pedido, un nuevo pedido cae en el libro de pedidos y ocupa su lugar en la lista de pedidos de su tipo.


Dado que solo los pedidos pendientes se incluyen en el libro de pedidos, para los pedidos de otros tipos debe seleccionar sus listas.


En todas las listas de pedidos, el lado de compra se debe ordenar en orden descendente, y el lado de venta se debe ordenar en orden ascendente. El primer elemento de la lista de órdenes limitadas para cada lado forma el mejor precio de oferta y demanda, respectivamente.


Otro punto importante es el orden de ejecución. El controlador debe implementar FIFO. Por lo tanto, si los precios de las dos ofertas coinciden, el creado anteriormente debería ser más alto.


En la interfaz de usuario, el libro parece una tabla que consta de un conjunto de niveles de precios, que presenta órdenes limitadas tanto para compra como para venta.



Para una distinción visual adicional, las aplicaciones para venta y compra tienen diferentes colores.


Agregación de nivel


Profundidad del libro: el número de niveles de precios. Para mercados activos con una gran cantidad de órdenes pendientes, separadas entre sí por una distancia mínima, la profundidad puede ser muy grande para mostrar en la terminal del comerciante. Para evaluar todo el libro, necesita una herramienta de agrupación de nivel.


Al cortar un decimal y agrupar los niveles, podemos reducir su número con cada paso.


Ejecución y cancelación de solicitudes.


Después de ejecutar o cancelar el pedido en el libro, el controlador debe actualizar el libro eliminando este pedido y notificando a todos los interesados ​​en los cambios en el libro.


Handler Architecture and Scaling


Dado el rendimiento y la confiabilidad requeridos, es necesario determinar enfoques para escalar aplicaciones y sistemas de almacenamiento.


Por lo general, los intercambios utilizan escalado vertical. El código para procesar aplicaciones y cuentas de usuario se ejecuta en una máquina dentro de un solo monolito. Tal enfoque muestra un buen rendimiento, pero tiene una limitación significativa: en cualquier caso, el escalado vertical tiene un límite, tanto en términos de potencia del procesador como de capacidad de almacenamiento.


Como parte del experimento, decidí que el procesamiento del mercado debería escalar horizontalmente. Cada herramienta individual es procesada por su propio proceso. Los procesos se distribuyen entre los nodos del clúster automáticamente. En caso de falla, el mercado se transfiere a otro nodo sin pérdida de estado.



La fórmula del sistema es extremadamente simple: los controladores M se distribuyen en los nodos K ​​del clúster y usan almacenamientos de datos L.
Un esquema similar le permite escalar el sistema a unos 150 nodos. Y cada controlador de mercado puede manejar aproximadamente 30k RPS.


Dado que el flujo de aplicaciones en todos los mercados es diferente y depende de la actividad del usuario, los mercados se pueden dividir en varios grupos: pequeños, medianos y grandes. Cada nodo tiene configuraciones que le permiten especificar límites en la cantidad de mercados que puede procesar. El asistente distribuye de forma automática y uniforme los mercados del mismo tipo entre los nodos del clúster. En caso de un cambio en la composición del clúster, los mercados se redistribuyen. Por lo tanto, se logra una distribución más o menos uniforme de la carga en el sistema.


Una vista de ejemplo de nodos en la interfaz de gestión de intercambio:



Almacenamiento de datos


El libro de pedidos cambia constantemente y debe mantenerse en la memoria. Para MVP, elegí Tarantool con WAL como el almacenamiento en memoria. Todos los datos históricos se registrarán en PostgreSQL.


El esquema para almacenar datos actuales e históricos debe corresponder al esquema seleccionado para escalar el código de los manejadores. Cada mercado puede usar sus propios postgres y tarantool. Para hacer esto, combine el par de postgresql y tarantool en una sola entidad: un almacén de datos de mercado.


Al configurar el mercado, el administrador tiene la capacidad de administrar repositorios. Para mantener la flexibilidad, en lugar de los detalles de acceso para instancias específicas de postgresql y tarantool, especificaremos un identificador único para el grupo de conexiones. La interfaz de estos grupos es compatible con la plataforma. Por lo tanto, el repositorio en la interfaz de administración se ve así:



Al configurar un mercado, el administrador debe especificar al menos una tienda para cada mercado. Si especifica algunos, obtendrá un mercado con replicación lógica de datos. Esta característica le permite configurar la confiabilidad y el rendimiento de un esquema de almacenamiento.


Datos del libro de pedidos


Tarantool usa el espacio para organizar los datos almacenados. La declaración de los espacios requeridos para el libro de pedidos es la siguiente:


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, }, } 

Dado que varios mercados pueden almacenar sus datos en una instancia de tarantool, agregaremos un identificador de mercado a todas las entidades. La implementación actual del libro se basa en el principio de contar una vez, muchas veces dar. Durante las operaciones de actualización de libros, los grupos se vuelven a contar automáticamente. Por ejemplo, agregamos un pedido al mercado, la precisión de los precios es 6, hay 6 grupos de precios posibles + un segmento con los datos del pedido original que deben actualizarse.


Existen muchas órdenes orders_mapping para emitir listas de órdenes de clientes activos.


Gracias al modelo de datos de tarantool, que utiliza una combinación de índices y varios iteradores de muestreo, el código lua que implementa el almacenamiento del libro de pedidos solo ocupa 600 líneas (junto con la inicialización).


Datos históricos


Los datos del mercado se almacenan en tablas separadas para cada mercado. Considere un conjunto de tablas base.


Historial de solicitudes completadas


Para guardar los resultados del procesamiento de aplicaciones, use la tabla de historial. Incluye aplicaciones completadas, así como canceladas, pero parcialmente completadas.


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

Sobre esta base, la emisión para usuarios finales se basa en el historial de sus ofertas.


Feed de datos históricos


Para fines de análisis, así como para la formación de una fuente de datos históricos, después de cada transacción, el controlador del mercado debe guardar información sobre este evento. Para arreglar eventos de cambios en el mercado, use la tabla 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) ) 

Almacena precios y volúmenes de mercado después de la transacción, y el campo de opciones contiene información de servicio, como una descripción de los pedidos involucrados en la transacción.


Feed de datos del gráfico


Para construir gráficos comerciales, la tabla de ticks es suficiente. Contiene el llamado flujo sin formato, pero postgresql tiene potentes funciones analíticas y le permite agregar datos bajo demanda.


Los problemas comienzan cuando hay demasiados datos y ya no hay suficiente energía. Para resolver, cree una tabla con datos precalculados:


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

Hablaremos sobre cómo trabajar con series de tiempo en Postgresql, preparar datos para la tabla df y cómo construir gráficos en el próximo artículo.


Resumen


Descubrimos los puntos principales de la organización del libro de pedidos y el mecanismo para procesar pedidos, así como también un poco inmersos en la práctica de trabajar con datos del mercado.


El esquema de almacenamiento seleccionado le permite comenzar desde una tienda para todos los mercados y, a medida que el proyecto crece, distribuir los mercados en diferentes tiendas, colocándolas lo más cerca posible de los procesadores del mercado.

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


All Articles