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:
- Performance-Attribution: Welcher Strategie-Typ, welches Symbol, welche Tageszeit, welche Marktphase trägt wie viel zur Gesamt-Performance bei.
- Pattern-Recognition: Welche Trade-Eigenschaften korrelieren mit Gewinn vs. Verlust? Lange-Hold-Trades, Trades nach Verlust-Tagen, Trades nach 16:00 Uhr.
- Verzerrungs-Detection: Wo neigen Sie zu systematischen Fehlern (z. B. zu früh ausgestiegen, größer gemacht nach Gewinnen)?
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:
- Sind Trades nach Verlust-Tagen größer als nach Gewinn-Tagen? (Revenge-Trading)
- Wie unterscheidet sich die Performance vor- und nachmittags?
- Wie wirkt sich Hold-Period auf P&L aus — zu früh aus dem Markt?
- Gibt es Streaks (5+ Verluste in Folge), die statistisch ungewöhnlich sind?
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.