σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademySkills
Ctrl K
AO
+1,2 Tsd. Lernende
AnmeldenBeitreten ↗
Skills
AO
+1,2 Tsd. Lernende
Anmelden
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python-Skill›Modul 10 · Hochleistungs-Python-Entwicklung›Thema 10.13·~50 Min.Fortgeschritten

Zeitreihendatenbanken

Position in der SkillModul 10 von 11 · Thema 13 von 13
10.13 Zeitreihendatenbanken
—Lab-Punkte— / 100XP gesamt—Serie—

Daten, die mit einer Uhr eintreffen

Ticks, Quotes, Risikozahlen und Servermetriken haben dieselbe Form: ein Zeitstempel, einige Labels, einige Messwerte und ein endloser Strom davon. Eine Zeitreihendatenbank (InfluxDB, TimescaleDB, QuestDB, kdb+) ist genau dafür gebaut. Schreibvorgänge sind fast immer Anhänge an der neuesten Zeit und daher schnell; Daten liegen in zeitlich geordneten Blöcken, sodass „die letzte Stunde“ wenig berührt; alter Detailgrad lässt sich zu gröberen Daten zusammenfassen und schließlich löschen. Das Datenmodell von InfluxDB ist typisch: eine Measurement (wie eine Tabelle), Tags (indizierte Labels wie Symbol oder Handelsplatz), Fields (die Messwerte) und ein Zeitstempel.

# One point in InfluxDB line protocol: measurement,tags fields timestamp
point = "trades,symbol=AAPL,venue=XNAS price=184.25,qty=100i 1700000000000000000"
head, fields, ts = point.split(" ")
measurement, *tag_pairs = head.split(",")
tags = dict(pair.split("=") for pair in tag_pairs)
print(measurement, tags)                             # trades {'symbol': 'AAPL', 'venue': 'XNAS'}
print(fields, int(ts))                               # price=184.25,qty=100i 1700000000000000000

Downsampling: von Ticks zu Bars

Jeden Tick ewig aufzubewahren ist teuer und selten nötig. Downsampling gruppiert Punkte in feste Zeitfenster und behält von jedem eine Zusammenfassung. Für Preise ist die übliche Zusammenfassung OHLCV: Open (erster Preis im Fenster), High, Low, Close (letzter Preis) und Volumen. Ein Tick zur Zeit ts gehört zum Fenster, das bei ts - ts % bucket beginnt. Datenbanken tun das fortlaufend und schreiben das Ergebnis in eine eigene, viel kleinere Tabelle (ein kontinuierliches Aggregat), die Charts und die meisten Backtests lesen, während die Roh-Ticks nur kurz aufbewahrt werden.

# Rolling ticks (ts, price, size) into 60-second OHLCV bars
def ohlcv_bars(ticks, bucket):
    bars = []
    for ts, price, size in ticks:
        start = ts - ts % bucket
        if bars and bars[-1]["start"] == start:
            bar = bars[-1]
            bar["high"], bar["low"] = max(bar["high"], price), min(bar["low"], price)
            bar["close"] = price
            bar["volume"] += size
        else:
            bars.append({"start": start, "open": price, "high": price, "low": price, "close": price, "volume": size})
    return bars

ticks = [(0, 100.0, 10), (30, 101.0, 5), (61, 99.5, 20), (65, 100.5, 1)]
for bar in ohlcv_bars(ticks, 60):
    print(bar)
# {'start': 0, 'open': 100.0, 'high': 101.0, 'low': 100.0, 'close': 101.0, 'volume': 15}
# {'start': 60, 'open': 99.5, 'high': 100.5, 'low': 99.5, 'close': 100.5, 'volume': 21}

As-of-Joins und Aufbewahrung

Zeitreihen ticken selten zu denselben Zeitpunkten, daher findet ein Join über gleiche Zeitstempel fast nichts. Die eigentliche Frage lautet „Wie lautete die Quote, als dieser Trade stattfand?“: Nehmen Sie für jeden Trade die letzte Quote zu seiner Zeit oder davor. Dieser As-of-Join (pandas.merge_asof, ASOF JOIN in kdb+ und DuckDB) darf nur zurückschauen; eine Quote nach dem Trade zu verwenden, lässt die Zukunft in die Analyse sickern. Da Quotes nach Zeit sortiert sind, findet eine binäre Suche (bisect_right) die richtige in logarithmischer Zeit. Schließlich verwirft eine Aufbewahrungsrichtlinie Punkte, die älter als ein Limit sind, damit der Speicher begrenzt bleibt: 30 Tage Ticks, ein Jahr Minutenbars und Tagesbars für immer.

# For each trade, the latest quote at or before it, found by binary search
from bisect import bisect_right

def asof_join(trades, quotes):
    times = [quote[0] for quote in quotes]
    joined = []
    for ts, price in trades:
        position = bisect_right(times, ts)
        joined.append((ts, price, *quotes[position - 1][1:]) if position else (ts, price, None, None))
    return joined

quotes = [(10, 99.9, 100.1), (40, 100.5, 100.7)]
print(asof_join([(5, 100.0), (10, 100.0), (50, 101.0)], quotes))
# [(5, 100.0, None, None), (10, 100.0, 99.9, 100.1), (50, 101.0, 100.5, 100.7)]
# Retention: keep only the points younger than the limit
def apply_retention(points, now, ttl):
    return [(ts, value) for ts, value in points if ts > now - ttl]

points = [(10, 1.0), (40, 2.0), (50, 3.0), (90, 4.0)]
print(apply_retention(points, 100, 60))              # [(50, 3.0), (90, 4.0)]   the point at 40 is exactly 60 s old: dropped
print(apply_retention(points, 100, 1000))            # everything is younger than the limit
Achten Sie auf die Kardinalität Ihrer TagsTags sind indiziert, daher erzeugt jede verschiedene Kombination von Tag-Werten eine eigene Serie. Ein Symbol oder Handelsplatz ist in Ordnung (Hunderte Werte); eine Order-ID oder einen Zeitstempel in einen Tag zu legen erzeugt Millionen Serien und kann die Datenbank in die Knie zwingen. Legen Sie Kennungen, die sich bei jedem Punkt ändern, in Fields, nicht in Tags. Speichern Sie Zeitstempel außerdem systemweit in UTC mit einer einzigen Genauigkeit (Sekunden, Millisekunden oder Nanosekunden), denn Mischen erzeugt Joins, die stillschweigend nichts treffen.
  • Ein Zeitreihenpunkt besteht aus Measurement, indizierten Tags, Messfeldern und Zeitstempel; Schreibvorgänge sind Anhänge, die Daten liegen in Zeitreihenfolge.
  • Verdichten Sie Ticks zu OHLCV-Bars (ts - ts % bucket) und lesen Sie die Bars; Roh-Ticks nur kurz aufbewahren.
  • Verbinden Sie Serien mit einem nur rückwärts schauenden As-of-Join (binäre Suche), wenden Sie Aufbewahrung an und halten Sie Tag-Kardinalität und Zeiteinheiten im Griff.

Als Nächstes: eine Python-Bibliothek paketieren, Ihren Code in etwas verwandeln, das andere installieren können.

Lab: Was bewertet wird

  • Mit ohlcv_bars Ticks zu Bars verdichten25 Pkt.
  • Mit asof_join die Quote zu jedem Trade finden25 Pkt.
  • Mit line_protocol einen Punkt schreiben25 Pkt.
  • Mit apply_retention abgelaufene Daten verwerfen25 Pkt.
Explorer · lab-129-1
▾ lab-129-1
…
bash · im Browser
learner@quantcraft:~/lab-129-1$
Python noch nicht geladenPythonLn 1, Col 1Spaces: 4UTF-8

Dieses Thema zurücksetzen?

Alles in lab-129-1 wird auf die Startdateien zurückgesetzt und das Terminal geleert. Ihre aktuelle Lösung geht verloren – das lässt sich nicht rückgängig machen.

Wird zurückgesetzt
  • main.py → Startcode
  • Terminal-Verlauf und Testausgabe
  • Nicht eingereichte Änderungen
Bleibt erhalten
  • Beste Lab-Punktzahl
  • Ihre XP und Ihre Serie
  • Vorlesungsfortschritt und Notizen

Melde dich an, um deinen Code auszuführen.

Alle Lektionen kannst du kostenlos lesen. Erstelle ein Konto oder melde dich an, um Code auszuführen, Tests zu starten und dieses Lab abzugeben.

  • IDE im Browser mit Bash-Terminal — nichts zu installieren
  • Sofortige Bewertung, XP und dein Platz auf der Skill
Konto erstellenAnmelden
ODER
Weiter mit GitHub