VonmoTrade-Experiment. Teil 4: Trading Charts


In früheren Artikeln haben wir herausgefunden, wie Handelsaufträge erstellt und verarbeitet werden. Das Thema dieses Artikels wird die Verarbeitung und Speicherung von Informationen sein, die für grafische Werkzeuge der Marktanalyse - Aktiencharts - erforderlich sind.


Bevor ich anfange, möchte ich einen kleinen Exkurs machen. Für interne Vonmo-Projekte wird das übliche V + -Wort-Benennungsschema verwendet, das die Funktionen des Projekts am besten beschreibt. Heute habe ich festgestellt, dass VTrade ein bestehendes Unternehmen ist. Um Verwirrung zu vermeiden, habe ich das Experiment in VonmoTrade umbenannt.


Zur Beurteilung der Marktlage reicht ein Auftragsbuch und eine Transaktionshistorie nicht aus. Wir brauchen ein Tool, mit dem wir die Entwicklung des Marktpreises klar und schnell erkennen können. Trading-Charts können in zwei Arten unterteilt werden:


  1. Linear;
  2. Intervall

Liniendiagramme


Der einfachste und verständlichste Zeitplan ohne Vorbereitung. Zeigt die Abhängigkeit des Preises eines Finanzinstruments von der Zeit an.



Der Hauptvorteil dieser Art von Graph ist die Einfachheit. Der Hauptnachteil ergibt sich daraus - geringer Informationsgehalt.


Wenn das Diagramm auf Rohdaten basiert, wird der Preis des letzten Abschlusses genommen. In der Regel werden Diagramme jedoch auf der Grundlage aggregierter Daten erstellt. In diesem Fall wird der Schlusskurs jedes Intervalls genommen. Da wir alles verwerfen, was in dem Intervall passiert ist, und nur den Schlusskurs des Intervalls nehmen, geht der Informationsgehalt verloren.


Grafikauflösung


Wenn wir damit beginnen, ein Diagramm auf der Grundlage aller Preisänderungen zu erstellen, das heißt, jede abgeschlossene Transaktion fällt in das Diagramm, ist es für eine Person schwierig, es wahrzunehmen. Und die für die Bearbeitung und Zustellung eines solchen Zeitplans aufgewendete Energie wird ineffizient eingesetzt.


Aus diesem Grund werden Daten ausgedünnt, indem die Zeitachse in Intervalle unterteilt und die Preise in diesen Intervallen aggregiert werden.
Auflösung - Die Größe des Elementarintervalls für die Teilung der Zeitachse: Sekunde, Minute, Stunde, Tag usw.


Bars


Beziehen Sie sich auf Intervalldiagramme. Um den Informationsgehalt zu erhöhen, müssen für jedes Zeitintervall am Anfang und Ende des Intervalls Preisinformationen sowie der Maximal- und Minimalpreis angezeigt werden. Die grafische Darstellung dieses Sets wird als Balken bezeichnet. Betrachten Sie das Schema eines Balkens:



Die Reihenfolge der Balken bildet ein Diagramm:



Japanische Kerzen


Beziehen Sie sich wie Balken auf Intervalldiagramme. Sie sind der beliebteste Diagrammtyp in der technischen Analyse. Eine Kerze besteht aus einem schwarzen oder weißen Körper und Schatten: oben und unten. Manchmal wird ein Schatten Docht genannt. Der obere und untere Rand des Schattens zeigt die Höchst- und Mindestpreise für den entsprechenden Zeitraum an. Die Grenzen des Körpers zeigen die Eröffnungs- und Schlusskurse. Lassen Sie uns eine Kerze zeichnen:



Die Reihenfolge der Kerzen bildet eine Grafik:



OHLCV-Notation


Im letzten Artikel haben wir das Datenspeicherschema für das Diagramm in postgresql herausgefunden und eine Tabelle für die Datenquelle erstellt, in der die aggregierten Daten gespeichert werden:


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

Felder müssen nicht erklärt werden, außer das Feld r ist die Auflösung der Reihe. Es gibt Aufzählungen in postgresql. Es ist praktisch, sie zu verwenden, wenn eine Reihe von Werten für ein Feld im Voraus bekannt ist. Durch die Aufzählungen definieren wir einen neuen Typ für die zulässigen Grafikauflösungen. Lassen Sie es eine Reihe von einer Minute bis zu einem Monat sein:


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

Es ist wichtig, ein Gleichgewicht zwischen der Leistung des Plattensystems, des Prozessors und den Gesamtbetriebskosten zu finden. Das System definiert derzeit 16 Auflösungen. Zwei Lösungen liegen auf der Hand:


  • Wir können alle Auflösungen zählen und in der Datenbank speichern. Diese Option bietet den Vorteil, dass beim Abtasten keine Zeit für die Aggregation von Intervallen aufgewendet wird und alle Daten sofort für die Ausgabe bereit sind. In einem Monat werden für ein Instrument etwas mehr als 72.000 Datensätze erstellt. Es sieht einfach und bequem aus, aber eine solche Tabelle ändert sich zu oft, da für jede Preisaktualisierung 16 Einträge in der Tabelle erstellt oder aktualisiert und der Index neu erstellt werden muss. In postgresql kann zusätzlich ein Problem mit der Garbage Collection auftreten.
  • Eine weitere Option ist das Speichern einer einzelnen Grundauflösung. Bei der Auswahl einer Basisauflösung müssen die erforderlichen Auflösungen erstellt werden. Wenn Sie beispielsweise die Minutenauflösung als Basis pro Monat speichern, werden für jedes Instrument 43.000 Datensätze erstellt. Gegenüber der Vorgängerversion reduziert sich somit das Aufnahme- und Overheadvolumen um 40%. Die Prozessorlast nimmt jedoch zu.

Wie oben erwähnt, ist es wichtig, ein Gleichgewicht zu finden. Daher besteht eine Kompromissoption darin, nicht eine Grundauflösung zu speichern, sondern mehrere: 1 Minute, 1 Stunde, 1 Tag. Mit diesem System werden monatlich 44,6 Tausend Datensätze für jedes Instrument erstellt. Die Optimierung der Aufnahmelautstärke beträgt 36%, die Prozessorauslastung ist jedoch akzeptabel. Um beispielsweise wöchentliche Intervalle zu erstellen, anstatt 10.080 Datensätze bei einer Grundauflösung von einer Minute zu lesen und zu aggregieren, müssen wir von der Festplatte lesen und die Daten von nur 7-Tage-Auflösungen aggregieren.


OHLCV-Lagerung


OHLCV ist von Natur aus eine Zeitreihe. Wie Sie wissen, eignet sich eine relationale Datenbank nicht sehr gut zum Speichern und Verarbeiten solcher Daten. Um diese Probleme zu lösen, verwendet das Projekt die Timescale- Erweiterung.


Timescale verbessert die Leistung von Einfüge- und Aktualisierungsvorgängen, ermöglicht die Konfiguration der Partitionierung und bietet Analysefunktionen, die speziell für die Arbeit mit Zeitreihen optimiert wurden.


Zum Erstellen und Aktualisieren von Balken benötigen wir nur Standardfunktionen:


  • date_trunc('minute' | 'hour' | 'day', transaction_ts) - um den Beginn des Intervalls der Auflösung von Minute, Stunde und Tag zu ermitteln.
  • greatest und least , um die Höchst- und Mindestpreise zu bestimmen.

Dank der Upsert-API wird nur eine Aktualisierungsanforderung pro Transaktion ausgeführt.
Ich habe diese Art von SQL zum Korrigieren von Marktänderungen in grundlegenden Auflösungen:


 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; 

Für die Abtastung zur Aggregation von Intervallen benötigen wir folgende Funktionen:


  • time_bucket - um in Intervalle zu brechen
  • first - um den Eröffnungspreis zu finden - O
  • max - der höchste Preis für das Intervall - H
  • min - der niedrigste Preis pro Intervall - L
  • last - um den Schlusskurs zu finden - C
  • sum - um das Handelsvolumen zu finden - V

Das einzige Problem, das bei der Verwendung von Timescale time_bucket sind die Einschränkungen der time_bucket Funktion. Sie können nur in Intervallen von weniger als einem Monat arbeiten. Um eine monatliche Auflösung zu erstellen, müssen Sie die Standardfunktion date_trunc verwenden.


API


Um Charts auf dem Client anzuzeigen, verwenden wir Lightweight-Charts von Tradingview. Die Bibliothek ermöglicht es Ihnen, das Erscheinungsbild der Diagramme vollständig anzupassen und ist bequem zu verwenden. Ich habe die folgenden Grafiken:



Da der Hauptteil der Interaktion zwischen dem Browser und der Plattform über den Websocket erfolgt, gibt es keine Probleme mit der Interaktivität.


Datenquelle


Die Datenquelle für die Diagramme (Datenfeed) sollte den erforderlichen Teil der Zeitreihe in der erforderlichen Auflösung zurückgeben. Gleichzeitig muss der Server die Punkte packen, um Datenverkehr zu sparen und die Verarbeitungszeit auf dem Client zu verkürzen.


Die Datenfeed-API muss zunächst so gestaltet werden, dass Sie mehrere Diagramme in einer Anforderung anfordern und deren Aktualisierungen abonnieren können. Dadurch wird die Anzahl der Befehle und Antworten im Kanal verringert.


Betrachten Sie ein Beispiel für die Anfrage der letzten 50 Minuten nach USDGBP mit einem automatischen Abonnement für Kartenaktualisierungen:


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

Sie können natürlich eine Reihe von Daten (von, bis) anfordern, aber da das Intervall der einzelnen Balken bekannt ist, erscheint mir die deklarative API, die den Zeitpunkt und die Anzahl der Balken angibt, praktischer.
Der Daten-Feed für diese Anfrage antwortet auf ähnliche Weise:


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

Das Feld bar_fields enthält Informationen zu den Positionen der Elemente. Weitere Optimierung besteht darin, dieses Feld in der Client-Konfiguration zu platzieren, die es beim Booten vom Server erhält.


Auf diese Weise erhält der Client den erforderlichen Teil der Verlaufsdaten und erstellt den Anfangszustand des Diagramms. Wenn sich der Status ändert, erhält er eine Aktualisierung, die nur den letzten Balken betrifft.


 { "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" } } } 

Vorläufiges Ergebnis


Während der gesamten Artikelserie haben wir die Theorie und Praxis des Aufbaus eines Austauschs analysiert. Es ist Zeit, das System zusammenzustellen.


Im nächsten Artikel befassen wir uns mit der Entwicklung grafischer Benutzeroberflächen: Service-UI für die Plattformverwaltung und UI für Endbenutzer. Eine Demoversion von Vonmo Trade wird ebenfalls vorgestellt.

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


All Articles