Brach viele Kreise auf der Suche nach einer Lösung, um schnell lange Preisverläufe für eine große Anzahl von Assets in Python zu erhalten. Ich hatte auch den Mut, mit Preisen in numpy Arrays arbeiten zu wollen, aber besser sofort in Pandas.
Standardansätze für die Stirn funktionierten enttäuschend, was dazu führte, dass die Abfrage für 30 Sekunden oder länger in der Datenbank ausgeführt wurde. Ich wollte es nicht ertragen und fand mehrere Lösungen, die mich vollkommen zufriedenstellten.
Beine wachsen aus der Objektnatur von Python heraus. Denn auch ganzzahlige Zahlen sind Objekte, was sich extrem negativ auf die Arbeitsgeschwindigkeit auswirkt. Ich wollte die Sprache kategorisch nicht ändern.
Die erste Lösung war eine Gruppierung der Preisentwicklung durch PostgreSQL, die zu einem leichten Leistungsabfall auf der Datenbankseite führte, die Aufgabe jedoch um etwa das Dreifache beschleunigte. Die Methode wird in einem anderen Artikel ausführlicher beschrieben.
Das Ergebnis war das Verständnis, dass Sie in Python irgendwie den gesamten Datensatz in einem Stück erhalten müssen, zumindest eine Zeichenfolge. Und durch Numpy-Arrays oder sofort in Pandas analysieren.
Die Endergebnisse:

Stirnlösung für PostgreSQL
Wir machen Datengruppierung in einer SQL-Abfrage. Ein Beispiel:
SELECT string_agg(symbol::text, ',') AS symbol_list , string_agg(dt::text, ',') AS dt_list , string_agg(open::text, ',') AS open_list , string_agg(high::text, ',') AS high_list , string_agg(low::text, ',') AS low_list , string_agg("close"::text, ',') AS close_list , string_agg(volume::text, ',') AS volume_list , string_agg(adj::text, ',') AS adj_list FROM v_prices_fast WHERE symbol IN ('{symbols}')
Das Parsen der Daten ist einfach:
{ 'symbol': np.array(r[0].split(',')),
Produktivität bei ~ 1,7 Millionen Linien:
%timeit get_prices_fast(is_adj=False)
Vorgefertigte Python-Pakete
Python ist gut für seine Community, die mit ähnlichen Problemen konfrontiert ist. Folgendes ist für unseren Zweck geeignet:
- odo - erstellt, um die Geschwindigkeit der Datenübertragung von einer Quelle zur anderen zu optimieren. Ganz in Python. Es interagiert mit PostgreSQL über SQLAlchemy.
- warp_prism - Die C-Erweiterung, die vom Quantopian-Projekt zum Abrufen von Daten aus PostgreSQL verwendet wird. Basis ist die Funktionalität von odo.
Beide Pakete nutzen die Fähigkeit von PostgreSQL, Daten in CSV zu kopieren:
COPY {query} TO :path WITH ( FORMAT CSV, HEADER :header, DELIMITER :delimiter, QUOTE :quotechar, NULL :na_value, ESCAPE :escapechar, ENCODING :encoding )
Die Ausgabe wird in pandas.DataFrame () oder numpy.ndarray () analysiert.
Da warp_prism in C geschrieben ist, hat es einen erheblichen Vorteil hinsichtlich der Analyse von Daten. Gleichzeitig hat es einen erheblichen Nachteil - begrenzte Unterstützung für Datentypen. Das heißt, es analysiert int, float, date und str, aber nicht numerisch. Odo hat keine solchen Einschränkungen.
Zur Verwendung ist es erforderlich, die Tabellenstruktur und die Abfrage mit dem Paket sqlalchemy zu beschreiben:
tbl_prices = sa.Table( 'prices', metadata, sa.Column('symbol', sa.String(16)), sa.Column('dt', sa.Date), sa.Column('open', sa.FLOAT), sa.Column('high', sa.FLOAT), sa.Column('low', sa.FLOAT), sa.Column('close', sa.FLOAT), sa.Column('volume', sa.BIGINT), sa.Column('adj', sa.FLOAT), ) query = sa.select(tbl_prices.c).where( tbl_prices.c.symbol.in_(SYMBOLS) ).order_by('symbol', 'dt')
Geschwindigkeitstests:
%timeit odo(query, pd.DataFrame, bind=engine)
warp_prism.to_arrays () - Vorbereitung eines Python-Wörterbuchs mit Numpy-Arrays.
Was kann man mit ClickHouse machen?
PostgreSQL ist für alle gut, außer für den Appetit auf Speichergröße und die Notwendigkeit, Sharding für große Tabellen zu konfigurieren. ClickHouse selbst splittert, speichert alles kompakt und arbeitet blitzschnell. Beispielsweise passt eine PostgreSQL-Tabelle mit einer Größe von ~ 5 GB in ClickHouse in ~ 1 GB. Die Verwendung von ClickHouse zum Speichern von Preisen wird in einem anderen Artikel beschrieben.
Zu meinem Leidwesen hat Odo nicht geholfen, obwohl es eine Clickhouse-Erweiterung für SQLalchemie gibt. Die Erinnerung an die Geschwindigkeit von Clickhouse in der Konsole führte mich zu der Idee, durch die Erstellung eines separaten Prozesses auf die Datenbank zuzugreifen. Ich weiß, dass es langwierig und ressourcenintensiv ist, aber die Ergebnisse waren nicht zu loben.
sql = 'SELECT days.symbol, days.date, days.open/10000, days.high/10000, days.low/10000, days.close/10000, days.volume FROM days ' \ 'WHERE days.symbol IN (\'{0}\') ORDER BY days.symbol, days.date;'.format("','".join(SYMBOLS)) cmd = 'clickhouse-client --query="{0}"'.format(sql) def ch_pandas(cmd): p = subprocess.Popen([cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) return pd.io.parsers.read_csv(p.stdout, sep="\t", names=['symbol', 'date', 'open', 'high', 'low', 'close', 'volume'])
Ergebnis:
%timeit ch_pandas(cmd)
ClickHouse HTTP-Portanforderung
Die Ergebnisse haben sich beim direkten Zugriff auf Port 8123, auf dem die Datenbank antwortet, etwas verschlechtert:
import urllib %timeit pd.io.parsers.read_csv('http://localhost:8123/?{0}'.format(urllib.parse.urlencode({'query': sql})), sep="\t", names=['symbol', 'date', 'open', 'high', 'low', 'close', 'volume'])
Aber nicht ohne eine Fliege in der Salbe.
Fliegen Sie mit ClickHouse in die Salbe
Die Datenbank war bei großen Stichproben beeindruckend, bei kleinen Ergebnissen jedoch enttäuschend. ~ 20 mal schlimmer als odo. Dies sind jedoch die Kosten für ein zusätzliches Kit mit dem Start des Prozesses oder dem Zugriff über HTTP.
Ergebnisse:

Fazit
Mit diesem Artikel ist das Streben nach einer Beschleunigung der Interaktion zwischen Python und Datenbanken beendet. Für PostgreSQL mit Standardfeldern und der Notwendigkeit eines universellen Zugriffs auf Preise ist es am besten, das Paket warp_prism von Quantopian zu verwenden. Wenn Sie große Verlaufsvolumina und eine hohe Häufigkeit von Anforderungen für eine große Anzahl von Zeilen speichern müssen, ist ClickHouse ideal.