Expérience VonmoTrade. Partie 3: Livre des mandats. Traitement et stockage des informations commerciales


Dans le dernier article du cycle, nous nous sommes familiarisés avec les types d'ordres de change. Aujourd'hui, nous analyserons le livre des commandes, le traitement des demandes et les questions liées à l'organisation du stockage des informations commerciales.


Offre et demande


Vous vous souvenez sûrement de la loi de l'offre et de la demande au cours de l'économie, qui montre la mécanique du marché pour la formation des prix:



La mĂȘme mĂ©canique travaille sur les Ă©changes.


Le carnet de commandes est une liste dans laquelle les commandes limitĂ©es des vendeurs et des acheteurs sont saisies, montrant ainsi l'intĂ©rĂȘt actuel pour un instrument financier particulier.


Si vous convertissez le graphique précédent par rapport au carnet de commandes, vous obtenez quelque chose comme ceci:



Nous voyons ici que le prix du marchĂ© est obtenu lorsque le prix de demande maximum et le prix d'offre minimum sont Ă©gaux. L'Ă©cart est la diffĂ©rence entre ces prix. Il s'agit d'un indicateur important, car il est associĂ© Ă  la liquiditĂ© de l'instrument. Plus l'Ă©cart est petit, plus l'instrument est liquide. Pour assurer la liquiditĂ© dans le cadre des Ă©changes, ils imposent souvent une limite Ă  l'Ă©cart maximum, au-dessus duquel les Ă©changes peuvent ĂȘtre interrompus.


Création d'applications


Considérez le cycle de vie d'une demande depuis l'admission à l'échange jusqu'à son exécution ou son annulation. Pour simplifier, nous considérerons le cas du marché des changes. Un processus spécial est responsable de la logique de traitement des ordres, appelons-le contrÎleur de marché.


Ainsi, le participant crĂ©e une application, il arrive Ă  l'Ă©change. Le responsable du traitement doit s'assurer que le participant dispose de suffisamment de liquiditĂ©s pour crĂ©er le type d'ordre demandĂ©. La source d'informations peut ĂȘtre un service de comptabilitĂ© interne ou toute API externe.


Pour l'exécution immédiate de cette application, une contre-application dite appariée doit exister sur le marché.


S'il y a un contre-ordre, de la paire trouvée, le plus petit ordre est exécuté en totalité et le plus grand en partie. Bien sûr, si une exécution partielle est autorisée par les instructions de trading de l'application. En l'absence de contre-commande, une nouvelle commande entre dans le carnet de commandes et prend sa place dans la liste des commandes de son type.


Étant donnĂ© que seules les commandes en attente entrent dans le carnet de commandes, pour les commandes d'autres types, vous devez sĂ©lectionner vos listes.


Dans toutes les listes de commandes, le cĂŽtĂ© achat doit ĂȘtre triĂ© par ordre dĂ©croissant et le cĂŽtĂ© vente doit ĂȘtre triĂ© par ordre croissant. Le premier Ă©lĂ©ment de la liste des ordres Ă  cours limitĂ© pour chaque cĂŽtĂ© constitue respectivement le meilleur prix de l'offre et de la demande.


Un autre point important est l'ordre d'exĂ©cution. Le contrĂŽleur doit implĂ©menter FIFO. Par consĂ©quent, si les prix des deux offres coĂŻncident, l'offre crĂ©Ă©e prĂ©cĂ©demment devrait ĂȘtre plus Ă©levĂ©e.


Dans l'interface utilisateur, le livre ressemble à un tableau composé d'un ensemble de niveaux de prix, qui présente des ordres à cours limité pour l'achat et la vente.



Pour une distinction visuelle supplémentaire, les demandes de vente et d'achat ont des couleurs différentes.


Agrégation de niveaux


Profondeur du livre - Le nombre de niveaux de prix. Pour les marchĂ©s actifs avec un grand nombre d'ordres en attente, sĂ©parĂ©s par une distance minimale, la profondeur peut ĂȘtre trĂšs grande pour ĂȘtre affichĂ©e dans le terminal du trader. Pour Ă©valuer l'ensemble du livre, vous avez besoin d'un outil de mise Ă  niveau.


En coupant une décimale et en regroupant les niveaux, nous pouvons réduire leur nombre à chaque étape.


Exécution et annulation des candidatures


Une fois la commande dans le livre exécutée ou annulée, le responsable du traitement doit mettre à jour le livre en supprimant cette commande et en informant toutes les personnes intéressées par les modifications du livre.


Architecture et mise Ă  l'Ă©chelle du gestionnaire


Compte tenu des performances et de la fiabilité requises, il est nécessaire de déterminer des approches pour faire évoluer les applications et les systÚmes de stockage.


En rÚgle générale, les échanges utilisent une mise à l'échelle verticale. Le code de traitement des applications et des comptes d'utilisateurs est exécuté sur une seule machine dans un seul monolithe. Une telle approche montre de bonnes performances, mais a une limitation importante - dans tous les cas, la mise à l'échelle verticale a une limite, à la fois en termes de puissance du processeur et de capacité de stockage.


Dans le cadre de l'expĂ©rience, j'ai dĂ©cidĂ© que le traitement du marchĂ© devrait Ă©voluer horizontalement. Chaque outil individuel est traitĂ© par son propre processus. Les processus sont rĂ©partis automatiquement entre les nƓuds de cluster. En cas d'Ă©chec, le marchĂ© est transfĂ©rĂ© vers un autre nƓud sans perte de statut.



La formule du systĂšme est extrĂȘmement simple: M gestionnaires sont rĂ©partis sur K nƓuds du cluster et utilisent L stockages de donnĂ©es.
Un schĂ©ma similaire vous permet de faire Ă©voluer le systĂšme Ă  environ 150 nƓuds. Et chaque contrĂŽleur de marchĂ© peut gĂ©rer environ 30 000 RPS.


Étant donnĂ© que le flux d'applications sur tous les marchĂ©s est diffĂ©rent et dĂ©pend de l'activitĂ© des utilisateurs, les marchĂ©s peuvent ĂȘtre divisĂ©s en plusieurs groupes: petits, moyens et grands. Chaque nƓud a des paramĂštres qui vous permettent de spĂ©cifier des limites sur le nombre de marchĂ©s qu'il peut traiter. L'assistant rĂ©partit automatiquement et uniformĂ©ment les marchĂ©s du mĂȘme type entre les nƓuds de cluster. En cas de modification de la composition des clusters, les marchĂ©s sont redistribuĂ©s. Ainsi, une distribution plus ou moins uniforme de la charge sur le systĂšme est obtenue.


Un exemple de vue des nƓuds dans l'interface de gestion des Ă©changes:



Stockage de données


Le carnet de commandes est en constante Ă©volution et doit ĂȘtre conservĂ© en mĂ©moire. Pour MVP, j'ai choisi Tarantool avec WAL comme stockage en mĂ©moire. Toutes les donnĂ©es historiques seront enregistrĂ©es dans PostgreSQL.


Le schéma de stockage des données actuelles et historiques doit correspondre au schéma sélectionné pour la mise à l'échelle du code des gestionnaires. Chaque marché peut utiliser ses propres postgres et tarantool. Pour ce faire, combinez la paire de postgresql et tarantool en une seule entité - un entrepÎt de données de marché.


Lors de la mise en place du marché, l'administrateur a la possibilité de gérer des référentiels. Pour maintenir la flexibilité, au lieu d'accéder à des instances spécifiques de postgresql et tarantool, nous spécifierons un identifiant de pool de connexions unique. L'interface de ces pools est prise en charge par la plateforme. Ainsi, le référentiel dans l'interface d'administration ressemble à ceci:



Lors de la configuration d'un marché, l'administrateur doit spécifier au moins un magasin pour chaque marché. Si vous en spécifiez quelques-uns, vous obtenez un marché avec une réplication logique des données. Cette fonctionnalité vous permet de configurer la fiabilité et les performances d'un schéma de stockage.


Données du carnet de commandes


Tarantool utilise l'espace pour organiser les données stockées. La déclaration des espaces requis pour le carnet de commandes est la suivante:


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

Étant donnĂ© que plusieurs marchĂ©s peuvent stocker leurs donnĂ©es sur une seule instance de tarantool, nous ajouterons un identifiant de marchĂ© Ă  toutes les entitĂ©s. La mise en Ɠuvre actuelle du livre est construite sur le principe de compter une fois, de donner plusieurs fois. Lors des opĂ©rations de mise Ă  jour des livres, les regroupements sont automatiquement recomptĂ©s. Par exemple, nous ajoutons une commande au marchĂ©, la prĂ©cision des prix est de 6, il existe 6 groupes de prix possibles + une tranche avec les donnĂ©es de commande d'origine qui doivent ĂȘtre mises Ă  jour.


Il existe de nombreuses commandes Orders_Mapping pour Ă©mettre des listes de commandes client actives.


Grùce au modÚle de données tarantool, utilisant une combinaison d'indices et de divers itérateurs d'échantillonnage, le code lua qui implémente le stockage du carnet de commandes ne prend que 600 lignes (avec l'initialisation).


Données historiques


Les données du marché sont stockées dans des tableaux séparés pour chaque marché. Considérons un ensemble de tables de base.


Historique des candidatures complétées


Pour enregistrer les résultats du traitement des demandes, utilisez la table d'historique. Il comprend les demandes entiÚrement remplies, ainsi que les demandes annulées, mais partiellement complétées.


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

Sur sa base, l'émission pour les utilisateurs finaux est basée sur l'historique de leurs offres.


Flux de données historiques


À des fins d'analyse, ainsi que pour la formation d'un flux de donnĂ©es historiques, aprĂšs chaque transaction, le contrĂŽleur de marchĂ© doit enregistrer des informations sur cet Ă©vĂ©nement. Pour corriger les Ă©vĂ©nements de changements du marchĂ©, utilisez le tableau des 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) ) 

Il stocke les prix et les volumes du marché aprÚs la transaction, et le champ opts contient des informations de service, telles qu'une description des commandes impliquées dans la transaction.


Flux de données du graphique


Pour construire des graphiques de trading, le tableau des ticks suffit. Il contient ce que l'on appelle le flux brut, mais postgresql possÚde de puissantes fonctions analytiques et vous permet d'agréger des données à la demande.


Les problÚmes commencent lorsqu'il y a trop de données et qu'il n'y a déjà pas assez de puissance. Pour résoudre, créez un tableau avec des données pré-calculées:


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

Nous expliquerons comment travailler avec des séries chronologiques dans Postgresql, préparer des données pour la table df et comment créer des graphiques dans le prochain article.


Résumé


Nous avons compris les principaux points de l'organisation du livre des commandes et du mécanisme de traitement des commandes, ainsi qu'un peu plongé dans la pratique du travail avec les données du marché.


Le schéma de stockage sélectionné vous permet de partir d'un magasin pour tous les marchés et, à mesure que le projet se développe, de répartir les marchés dans différents magasins, en les plaçant le plus prÚs possible des processeurs de marché.

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


All Articles