← Alle Insights

Trading-Journal mit Python: vom CSV zur Erkenntnis.

Excel-Tabellen mit Trade-Listen sind kein Trading-Journal — das ist Buchhaltung. Ein echtes Journal beantwortet Fragen, die das Brokerage-Statement nie beantwortet: Welche Setups verdienen wirklich? Welche Tageszeit ist Ihre beste? Wann sind Sie statistisch dumm geworden?

Was ein Trading-Journal können muss.

Drei Funktionen, die in 90 % der manuellen Excel-Lösungen fehlen:

Datenmodell.

Eine SQLite-Datenbank mit drei Tabellen reicht für 99 % der Anwendungsfälle:

CREATE TABLE trades (
    id            INTEGER PRIMARY KEY,
    strategy      TEXT,
    symbol        TEXT,
    side          TEXT,         -- 'long' / 'short'
    entry_time    TIMESTAMP,
    exit_time     TIMESTAMP,
    entry_price   REAL,
    exit_price    REAL,
    quantity      REAL,
    pnl           REAL,
    commission    REAL,
    setup_type    TEXT,         -- z.B. 'breakout', 'mean-rev'
    market_regime TEXT,         -- z.B. 'trending', 'choppy'
    notes         TEXT
);

CREATE TABLE strategies (
    name          TEXT PRIMARY KEY,
    description   TEXT,
    initial_capital REAL,
    started_at    DATE,
    status        TEXT          -- 'live', 'paper', 'archived'
);

CREATE TABLE journal_entries (
    id            INTEGER PRIMARY KEY,
    date          DATE,
    mood          INTEGER,      -- 1-10
    market_view   TEXT,
    learnings     TEXT,
    next_actions  TEXT
);

Die journal_entries-Tabelle ist optional, aber sehr empfohlen. Tägliche oder wöchentliche Reflexionen schaffen über Monate ein Muster, das Performance-Daten allein nicht zeigen.

Daten-Import vom Broker.

Die meisten Broker bieten CSV-Exports der Trade-Historie. IBKR liefert besonders saubere Daten über das FlexQuery-System. Ein Beispiel-Import in Python:

import pandas as pd
import sqlite3

def import_ibkr_trades(csv_path, db_path):
    df = pd.read_csv(csv_path)
    df = df.rename(columns={
        'DateTime': 'entry_time',
        'Symbol':   'symbol',
        'Quantity': 'quantity',
        'TradePrice': 'entry_price',
        'IBCommission': 'commission',
        'NetCash':  'pnl',
    })
    df['strategy'] = df['symbol'].map(strategy_lookup)
    df['side'] = df['quantity'].apply(
        lambda q: 'long' if q > 0 else 'short')

    conn = sqlite3.connect(db_path)
    df.to_sql('trades', conn, if_exists='append', index=False)
    conn.close()

Reports, die wirklich nützen.

Wöchentlicher Performance-Report

Nicht „diese Woche +1,2 %". Sondern: pro Strategie, pro Symbol, pro Setup-Typ. Was läuft, was nicht — und ist die Abweichung statistisch signifikant?

def weekly_report(db_path, week_ending):
    conn = sqlite3.connect(db_path)
    df = pd.read_sql_query(
        "SELECT * FROM trades WHERE exit_time BETWEEN ? AND ?",
        conn, params=(week_ending - pd.Timedelta(7, 'd'), week_ending))

    by_strat = df.groupby('strategy').agg(
        n_trades=('id', 'count'),
        win_rate=('pnl', lambda x: (x > 0).mean()),
        avg_win=('pnl', lambda x: x[x > 0].mean()),
        avg_loss=('pnl', lambda x: x[x < 0].mean()),
        total_pnl=('pnl', 'sum'),
        sharpe=('pnl', lambda x: x.mean() / x.std() * np.sqrt(252))
    )
    return by_strat

Verzerrungs-Detection

Eine kleine Routine, die nach typischen Fehlern sucht:

Diese Berechnungen sollten automatisiert wöchentlich laufen und auf einem Dashboard (Grafana, Streamlit, eigenes HTML) angezeigt werden.

Bonus: Trade-Replay.

Für besonders interessante (gewinnbringende oder verlust-bringende) Trades: Chart zum Zeitpunkt des Entries und Exits exportieren, mit den eigenen Trade-Markern. Hilft, im Nachhinein zu sehen, ob das Setup wirklich da war oder ob die Erinnerung schönt.

import mplfinance as mpf

def replay_trade(trade_id, db_path, price_data):
    # Trade-Daten laden
    conn = sqlite3.connect(db_path)
    trade = pd.read_sql_query(
        "SELECT * FROM trades WHERE id = ?", conn,
        params=(trade_id,)).iloc[0]

    # Kontext: 5 Tage vor Entry bis 2 Tage nach Exit
    ctx = price_data.loc[
        trade['entry_time'] - pd.Timedelta('5d')
        : trade['exit_time'] + pd.Timedelta('2d')]

    # Plot mit Entry/Exit-Marker
    adds = [
        mpf.make_addplot([trade['entry_price']], type='scatter',
                         markersize=200, marker='^', color='g'),
        mpf.make_addplot([trade['exit_price']], type='scatter',
                         markersize=200, marker='v', color='r'),
    ]
    mpf.plot(ctx, type='candle', addplot=adds,
             title=f"{trade['symbol']} — {trade['strategy']}")

Was ich gelernt habe.

Mein erster Versuch eines Trading-Journals war eine Excel-Tabelle mit 30 Spalten. Sie war perfekt — und unbrauchbar, weil sie nichts beantwortete, was ich wissen wollte. Erst als ich angefangen habe, gezielt Fragen zu formulieren („Sind meine Donnerstags- Trades schlechter als die übrigen?") und dann das Datenmodell entsprechend zu bauen, wurde aus dem Journal ein echtes Werkzeug.

Bauen Sie nicht ein Journal und suchen danach Fragen. Bauen Sie aus Fragen ein Journal.

Sie wollen ein Trading-Journal aufbauen, das wirklich Erkenntnisse liefert? Erstgespräch buchen — wir bauen es passend zu Ihrem Setup.