Code-Starterpaket für Trader — Python und Pine Script

Sechs kopierbare Codebeispiele: vier in Python (CSV-Journal laden, Kennzahlen, Ergebniskurve mit Drawdown und ATR-Positionsrechner) und zwei in Pine Script (Tagesspanne im Verhältnis zur ATR mit Alarm und Handelsplan-Zeitfenster im Chart). Es sind ausschließlich ANALYSEWERKZEUGE — sie rechnen, zeichnen und erinnern; keines erzeugt Kauf-/Verkaufssignale oder trifft Entscheidungen, denn das ist Aufgabe des Playbooks. API-Namen und ausführbare Bezeichner bleiben englisch und entsprechen damit der Dokumentation von pandas und Pine Script sowie üblichen Fehlermeldungen. In dieser Sprachfassung sind menschliche Kommentare und Ausgabebezeichnungen übersetzt; auch die KI-Werkstatt berücksichtigt bei Code-Prompts diese Unterscheidung. Die Erläuterungen verwenden die Kurssprache und führen Schritt für Schritt durch die Anwendung. Umgebung: Python-Installation und erste Ausführungen behandelt 11.8; die Journal-Spaltenüberschriften stehen in Anhang F.

Arbeitsplatz eines Traders und Analysten: Codeeditor mit Python-Skripten zur Journalanalyse und Chart mit Pine-Script-Indikatoren auf dem Monitor
Das Wichtigste in 60 Sekunden
  • Werkzeuge, keine Strategien: Der Code berechnet Kennzahlen aus IHREN Daten und überwacht IHREN Plan — dieselbe Grenze gilt im Abschnitt „Was KI nicht tut“ in Anhang H, aus denselben Gründen
  • Gemeinsames Datenformat: Die Python-Beispiele lesen die Überschriften des Journal-Arbeitsblatts (Anhang F) — die Trefferquote zählt Break-even-Trades im Nenner, und der Erwartungswert nutzt Nettoergebnisse; dieselben Definitionen wie in Prompt P5
  • Pine Script ohne Lookahead: S5 ruft Tagesdaten mit lookahead=barmerge.lookahead_off ab; S6 greift überhaupt nicht auf einen höheren Zeitrahmen zu (nur auf die Funktion time ). Fehlender Lookahead beweist kein fehlendes Repainting — die S5-Beschreibung erläutert die Anzeige auf historischen und laufenden Kerzen. Genau solche Fallstricke sucht die Codeprüfung mit Prompt P3
  • Der Rechner rundet Volumen AB: Beim Umrechnen von Risiko in Lots erhöht Aufrunden das Risiko systematisch über den Plan hinaus — diese eine Codezeile unterscheidet 1% von „ungefähr 1%“

Vor der Codeausführung: drei Regeln

  • Vor dem Ausführen lesen. Jedes Beispiel ist bewusst kurz — Sie sollten jede Zeile verstehen. Unverstandenen Code mithilfe einer P3-Prüfung ändern, nicht durch Raten; für Beispiele aus Foren und Chats gilt diese Regel doppelt.
  • Dateien liefern Daten; der Code liefert keine Entscheidungen. Die Skripte lesen einen Journalexport (Format aus Anhang F) und ausdrücklich von Ihnen gelieferte Parameter. Sie erzeugen Zahlen und Charts für den Wochenrückblick — Schlussfolgerungen ziehen Sie nach dem Auditverfahren in 15.10 unter Berücksichtigung von Stichprobengröße, Abhängigkeiten zwischen Trades und Ergebnisunsicherheit.
  • Die Umgebung aus 11.8: Python 3.11 oder neuer mit pandas und matplotlib (Installation und erstes Skript stehen dort); S1–S4 wurden am 17. September 2026 mit Python 3.12.10, pandas 3.0.5 und matplotlib 3.11.1 erneut ausgeführt. Andere Versionen wurden nicht getestet. Pine Script in den Indikatoreditor Ihrer Chartplattform einfügen; beide Indikatoren nutzen Version 6.

Mindestschritte zur Ausführung: In Ihrer Python-3.11+-Umgebung die Version mit python --version prüfen, dann Pakete mit python -m pip install pandas==3.0.5 matplotlib==3.11.1 installieren, S1+S2+S3 als analiza_dziennika.py und die Test-CSV als dziennik.csv speichern und anschließend python analiza_dziennika.py ausführen. S4 benötigt nur die Standardbibliothek. S5 und S6 separat in Pine Editor einfügen. Beide Pine-Beispiele wurden anhand der v6-Dokumentation geprüft; sie wurden nicht kompiliert und ihre Alarme nicht in TradingView getestet.

Analyseablauf: Ein CSV-Journalexport geht in Python-Skripte ein, Kennzahlen und Ergebniskurve kommen heraus; die Schlussfolgerungen fließen in Wochenrückblick und Playbook ein CSV-JournalFormat aus Anhang F Python: S1–S3Kennzahlen · Kurve · Drawdown EOW-RückblickStichprobe, Abhängigkeiten, Unsicherheit PlaybookRegeln und Tests versionieren In diesem Ablauf fehlt bewusst ein Pfeil „Code → Order“

S1 · CSV-Journal laden

Python 3 · pandas

Einstiegspunkt für S2 und S3: Liest den Arbeitsblattexport aus Anhang F, prüft Überschriften und Feldanzahl und bereinigt anschließend die Spalte r_net . Doppelte Spaltennamen, leere Überschriften sowie zu viele oder zu wenige Felder stoppen den Import; das Skript entscheidet nicht zwischen zwei Ergebnisspalten und verschiebt keine Daten in den Index. Ein leeres Feld durch ein Trennzeichen darstellen, zum Beispiel ;A+;;0; eine abgeschnittene Zeile ist ein anderer Fall. Trenn- und Dezimalzeichen für die CSV-Variante einstellen (PL: Semikolon und Komma; EN: Komma und Punkt), ebenso die Kodierung: Dateien aus dem Generator in Anhang F nutzen UTF-8; auch die in Excel gespeicherte Variante „CSV UTF-8“ ist als UTF-8 lesbar; ein älterer Export mit polnischer Codepage kann cp1250 erfordern. Speicherformat prüfen, denn die Oberflächensprache allein bestimmt die Kodierung nicht. Ein leeres r_net und ein leeres close_time_utc bedeuten, dass der Trade noch offen ist (und übersprungen wird); eine Schließungszeit ohne Ergebnis bedeutet einen unvollständigen Datensatz und stoppt die Berechnung. Ein nicht leerer, nicht numerisch lesbarer Wert (auch „NA“ oder ein Strich) stoppt das Skript mit Datensatznummer — ebenso ein geschlossener Trade ohne Schließungszeit im Format von Anhang F (2026-07-08 13:15), da die Kurvenreihenfolge sonst geraten werden müsste.

import pandas as pd

CSV_PATH = "dziennik.csv"   # Journalexport (Struktur aus Anhang F)
CSV_SEP  = ";"              # ";" für CSV mit PL-Gebietsschema, "," für EN
DECIMAL  = ","              # "," für CSV mit PL-Gebietsschema, "." für EN
ENCODING = "utf-8-sig"      # Generator aus Anhang F schreibt UTF-8 (BOM berücksichtigt); ältere CSV mit polnischer Codepage kann "cp1250" benötigen

REQUIRED = ["r_net", "setup", "setup_class", "error_code", "close_time_utc"]

# Lokale Datei lesen und ihre Struktur VOR dem Aufbau eines DataFrame prüfen.
# pandas.read_csv kann doppelte Überschriften umbenennen oder aus Zusatzfeldern einen Index ableiten.
import csv

try:
    with open(CSV_PATH, encoding=ENCODING, newline="") as source:
        reader = csv.reader(source, delimiter=CSV_SEP, strict=True)
        header = next(reader, None)
        if not header:
            raise SystemExit("CSV nicht lesbar: Die Kopfzeile ist leer.")
        if any(not name or name != name.strip() for name in header):
            raise SystemExit("Leerer Spaltenname oder Leerzeichen an dessen Rändern — CSV-Kopfzeile korrigieren.")
        duplicates = sorted({name for name in header if header.count(name) > 1})
        if duplicates:
            raise SystemExit(f"Doppelte Spalten: {duplicates} — im Journal bereinigen.")
        rows, record_indexes = [], []
        for index, row in enumerate(reader):
            if not row:                              # ein vollständig leerer CSV-Datensatz
                continue
            if len(row) != len(header):
                raise SystemExit(f"Datensatz {index + 1}: erwartet {len(header)} Felder, erhalten {len(row)} "
                                 f"— Trennzeichen und Anführungszeichen prüfen; es wurden keine Felder verworfen.")
            rows.append(row)
            record_indexes.append(index)
    df = pd.DataFrame(rows, columns=header, index=record_indexes, dtype="string").replace("", pd.NA)
except UnicodeDecodeError:
    raise SystemExit(f"Dekodierung nicht möglich: {CSV_PATH} als {ENCODING} — UTF-8 oder ältere cp1250 prüfen.") from None
except (OSError, csv.Error) as exc:
    raise SystemExit(f"Nicht lesbar: {CSV_PATH}: {exc}") from None

missing = [c for c in REQUIRED if c not in df.columns]
if missing:
    raise SystemExit(f"Fehlende Spalten: {missing} — Überschriften mit Anhang F und Einstellung CSV_SEP abgleichen.")

raw = df["r_net"]                                   # ein leeres Ergebnis ist nur bei ebenfalls leerer Schließungszeit zulässig
txt = raw.astype("string").str.strip().str.replace(DECIMAL, ".", regex=False)   # Spalte selbst einlesen: Eine ungültige Zelle darf lesbare Werte nicht verdecken
r   = pd.to_numeric(txt, errors="coerce").astype("Float64")  # Ganzzahlüberlauf in späteren Summen vermeiden
r   = r.where(r.abs() != float("inf"))              # auch inf ist kein gültiges R-Multiple
invalid = raw.notna() & r.isna()                    # ausgefüllt, aber nicht als Zahl lesbar
if invalid.any():
    raise SystemExit(f"Ungültiges r_net in Datensätzen {(df.index[invalid] + 1).tolist()} "
                     f"(Datensatz 1 = erste Zeile unter der Kopfzeile) — Werte und DECIMAL prüfen; "
                     f"NA, Striche und Unendlich sind keine Zahlen.")

blank = df.isna().all(axis=1)                       # ganze Zeile leer = ungenutzte Tabellenzeile (XLSX-Ganzblattexport), kein Trade
df, r = df[~blank], r[~blank]
has_close = df["close_time_utc"].fillna("").str.strip().ne("")
incomplete = r.isna() & has_close
if incomplete.any():
    raise SystemExit(f"Fehlendes r_net für geschlossene Datensätze {(df.index[incomplete] + 1).tolist()} "
                     f"— Nettoergebnis vor Kennzahlberechnung ergänzen.")
open_trades = int(r.isna().sum())
df = df.assign(r_net=r).dropna(subset=["r_net"])   # Index beibehalten = Datensatznummern für die folgende Meldung

# Ergebniskurve und Drawdown benötigen Zeitreihenfolge (Anhang F: nach Schließungszeit) — geschlossener Trade ohne lesbare Schließungszeit stoppt das Skript
close_text = df["close_time_utc"].fillna("").str.strip()
has_time = close_text.str.fullmatch(r"\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?")
closed = pd.to_datetime(close_text.where(has_time), errors="coerce", format="ISO8601", utc=True)   # Anhang F schreibt 2026-07-08 13:15; festes Format macht die Prüfung zeilenreihenfolgeunabhängig; utc=True verhindert Abbruch durch unerwartetes Suffix "Z"/"+00:00"
if closed.isna().any():
    raise SystemExit(f"close_time_utc nicht lesbar in Datensätzen {(df.index[closed.isna()] + 1).tolist()} "
                     f"— jeder geschlossene Trade benötigt eine Schließungszeit im Format von Anhang F (2026-07-08 13:15); Journal korrigieren, nicht Skript.")
df = df.assign(_t=closed).sort_values("_t", kind="stable").drop(columns="_t").reset_index(drop=True)

print(f"Geladen: {len(df)} geschlossene Trades ({open_trades} Zeile(n) übersprungen: weder Schließungszeit noch r_net).")

Sie erhalten: einen DataFrame, df, für die nächsten Beispiele vorbereitet und nach Schließungszeit sortiert (Kurve und Drawdown werden in dieser Reihenfolge berechnet), sowie die Zahl übersprungener offener Trades. Eine Leerzeile bedeutet einen vollständig leeren Datensatz wie beim Export eines gesamten XLSX-Blatts — das Skript zählt diese Zeilen nicht. Eine Zeile mit Daten, aber ohne Ergebnis oder Schließungszeit wird als noch offen übersprungen; ein geschlossener Eintrag ohne Ergebnis stoppt das Skript. Bei Abbruch mit Datensatzliste die genannten Journalzeilen korrigieren, nicht das Skript; ist die gesamte Spalte betroffen, auch die Konstante DECIMAL prüfen. Die Datensatznummerierung beginnt nach der Überschrift und zählt leere Datensätze mit; ein Feld in Anführungszeichen kann Trennzeichen oder Zeilenumbruch enthalten und dennoch zum selben Datensatz gehören. Jeder Trade darf nur einmal vorkommen: Vor Zusammenführung von Exporten die Kennungen trade_id prüfen — S1 entfernt keine Duplikate aus der Historie. Ein Datum ohne Uhrzeit genügt nicht zur Rekonstruktion der Reihenfolge. Eine Zeit ohne Zeitzonenkennung gilt als UTC; ein ausdrücklicher ISO-8601-Offset wird in UTC umgerechnet. Bei identischen Zeiten bleibt die Datensatzreihenfolge erhalten — diese im Journal festlegen, denn sie beeinflusst den Drawdown-Verlauf.

S2 · Journal-Kennzahlen

Python 3 · pandas

Dieselben Definitionen wie in Prompt P5 und im Arbeitsblatt-Dashboard: Die Trefferquote zählt Break-even-Trades im Nenner; der Profit-Faktor liefert eine Meldung statt einer Division durch null; der Erwartungswert ist der Mittelwert von Netto-R (r_net); der Drawdown wird ab der kumulativen Kurve mit Ausgangspunkt 0 R gemessen (schon der erste Verlust ist ein Drawdown — wie in der Formel von Arbeitsblatt F). Nach S1 ausführen. Die Kennzahldefinitionen sind gleich, die Fehlerbehandlung unterscheidet sich aber von P5: S1 stoppt bei ungültigem Ergebnis eines geschlossenen Trades, während P5 diese Zeile überspringen und zählen lässt. Ergebnisse erst vergleichen, wenn feststeht, welche Trades beide Stichproben enthalten.

import math

def metrics(r):
    "Zentrale Journal-Kennzahlen für eine Reihe von Netto-R-Multiples.\n    Definitionen (wie im Dashboard von Anhang F):\n    - Trefferquote: Trades mit r > 0 geteilt durch ALLE gültigen Trades (Break-even bleibt im Nenner)\n    - Profit-Faktor: Summe positiver r geteilt durch abs(Summe negativer r)\n    - Erwartungswert: arithmetischer Mittelwert von r (nach Kosten)\n    - maximaler Drawdown: größter Rückgang der kumulativen R-Kurve von ihrem laufenden Höchststand,\n      mit Kurvenstart bei 0 R (ein erster Verlust von -1 R ist also schon 1 R Drawdown)"
    n = len(r)
    if n == 0:
        raise SystemExit("Keine gültigen Trades — nichts zu messen.")
    try:
        r = r.astype(float)
    except (TypeError, ValueError):
        raise SystemExit("Kennzahlen erfordern numerische Netto-R-Werte — zuerst S1 ausführen.") from None
    if not r.map(math.isfinite).all():
        raise SystemExit("Kennzahlen erfordern endliche Netto-R-Werte — zuerst S1 ausführen.")
    wins   = r[r > 0]
    losses = r[r < 0]
    equity   = r.cumsum()
    peak     = equity.cummax().clip(lower=0)   # Anhang F: MAX(0; laufender Höchststand)
    drawdown = peak - equity
    try:
        total = math.fsum(r)
        pf_value = None if losses.empty else math.fsum(wins) / abs(math.fsum(losses))
    except (OverflowError, ValueError, ZeroDivisionError):
        raise SystemExit("Zahlenbereich überschritten — R-Werte prüfen; keine Kennzahlen zurückgegeben.") from None
    if (not equity.map(math.isfinite).all() or not drawdown.map(math.isfinite).all()
            or (pf_value is not None and not math.isfinite(pf_value))):
        raise SystemExit("Zahlenbereich überschritten — R-Werte prüfen; keine Kennzahlen zurückgegeben.")
    pf = "keine Verluste in der Stichprobe" if pf_value is None else round(pf_value, 2)
    return {
        "trades":         n,
        "win_rate":       round(len(wins) / n, 3),
        "profit_factor":  pf,
        "expectancy_R":   round(total / n, 3),
        "sum_R":          round(total, 2),
        "max_drawdown_R": round(drawdown.max(), 2),
    }

for key, value in metrics(df["r_net"]).items():
    print(f"{key:>16}: {value}")

Sie erhalten: sechs Kennzahlen für den Wochenrückblick; ohne Verluste ist PF eine Meldung statt einer Zahl. Überschreiten Werte den Berechnungsbereich, stoppt das Skript die Analyse, statt inf, NaN oder einen scheinbar null betragenden Drawdown zurückzugeben. WR = 0.4 bedeutet 40%. PF wird in R berechnet, nicht PLN; bei wechselndem Geldrisiko können sich die Ergebnisse unterscheiden. Kosten sind bereits in r_net enthalten — S2 zieht sie nicht erneut ab. Der Mittelwert allein oder allein das Überschreiten von 20 oder 50 Trades bewertet keinen Handelsvorteil. Dashboard-Definitionen stehen in Anhang F, der Plan für Stichprobenauswahl, Kosten und Validierung in Anhang N.

S3 · Ergebniskurve und Drawdown

Python 3 · pandas + matplotlib

Zeichnet die kumulative R-Kurve mit dem Drawdown vom Höchststand darunter und speichert eine PNG neben der durch CSV_PATH festgelegten CSV-Datei (S1), unabhängig vom Verzeichnis, aus dem das Skript ausgeführt wird. Die Kurve zeigt Ergebnisreihenfolge und Verlusthäufungen, die in Gesamtzahlen verschwinden. Sie summiert R, ist weder Kontostand in PLN noch prozentuales Eigenkapital: Bei wechselndem Geldrisiko bildet sie Kontostandsänderungen nicht nach. Ein- und Auszahlungen sowie Ergebnisse offener Positionen fehlen. Nach S1 ausführen.

import math
import matplotlib.pyplot as plt
from pathlib import Path

if len(df) == 0:
    raise SystemExit("Keine geschlossenen Trades — nichts zu zeichnen.")

equity   = df["r_net"].astype(float).cumsum()
drawdown = equity.cummax().clip(lower=0) - equity   # dieselbe Definition wie S2 und Anhang F (Kurve beginnt bei 0 R)

if not equity.map(math.isfinite).all() or not drawdown.map(math.isfinite).all():
    raise SystemExit("Zahlenbereich überschritten — R-Werte prüfen; kein Chart gespeichert.")

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(9, 6), sharex=True,
                               gridspec_kw={"height_ratios": [3, 1]})
x = [0, *(equity.index + 1)]                        # Trade 0 = Ausgangspunkt 0 R, ab dem der Drawdown gemessen wird
ax1.plot(x, [0.0, *equity.values])
ax1.set_ylabel("Ergebnis (R)")
ax1.set_title("Ergebniskurve — kumulierte Netto-R")
ax1.grid(True, alpha=0.3)

ax2.fill_between(x, [0.0, *(-drawdown.values)], 0, alpha=0.5)
ax2.set_ylabel("Drawdown (R)")
ax2.set_xlabel("Trade-Nr.")
ax2.grid(True, alpha=0.3)

fig.tight_layout()
PNG_PATH = Path(CSV_PATH).with_name("equity_curve.png")   # neben der Journaldatei, unabhängig vom Arbeitsverzeichnis
fig.savefig(PNG_PATH, dpi=150)
plt.close(fig)
print(f"Gespeichert: {PNG_PATH}")

Sie erhalten: eine Datei equity_curve.png neben dem Journal; die Kurve beginnt bei 0 R (Trade 0), dem Ausgangspunkt der Drawdown-Messung. Der untere Bereich zeigt über die gesamte Historie den Rückgang kumulierter R vom Höchststand (ab 0 R wie in S2 und Arbeitsblatt F) — keinen Wochenverlust. Das Wochenlimit des Plans (Anhang E) separat Woche für Woche in der dort festgelegten Einheit und nach der festgelegten Rücksetzregel prüfen.

Testdatei und erwartete Ausgabe (S1 + S2 + S3) CSV · PL-Format

Als dziennik.csv (UTF-8) neben dem kombinierten S1+S2+S3-Skript speichern und ausführen. Die Datei enthält nur die von S1 benötigten Spalten — der vollständige Export aus Anhang F (26 Spalten) wird genauso geladen. Die letzte Zeile ist ein offener Trade (r_net leer); die ersten beiden geschlossenen Trades sind Verluste, deshalb wird Drawdown ab 0 R gemessen.

trade_id;close_time_utc;setup;setup_class;r_net;error_code
2026-041;2026-07-08 13:15;wybicie-azja-londyn;A+;-1,00;5
2026-042;2026-07-09 16:40;powrot-do-sredniej;B;-1,08;3
2026-043;2026-07-10 09:05;wybicie-azja-londyn;A+;1,83;0
2026-044;2026-07-13 12:20;wybicie-azja-londyn;B;0,00;0
2026-045;2026-07-14 14:30;powrot-do-sredniej;C;2,40;0
2026-046;;wybicie-azja-londyn;B;;

Erwartete Ausgabe (Python 3.12.10, pandas 3.0.5, matplotlib 3.11.1; Ausführung am 17. September 2026):

Geladen: 5 geschlossene Trades (1 Zeile(n) übersprungen: weder Schließungszeit noch r_net).
          trades: 5
        win_rate: 0.4
   profit_factor: 2.03
    expectancy_R: 0.43
           sum_R: 2.15
  max_drawdown_R: 2.08
Gespeichert: equity_curve.png

Manuell nachrechnen: Gewinne 1.83 + 2.40 = 4.23 R, Verluste 1.00 + 1.08 = 2.08 R → PF 2.03; insgesamt 2.15 R / 5 = 0.43 R; Kurve −1.00 → −2.08 → −0.25 → −0.25 → 2.15 mit Anfangshöchststand 0 R → Drawdown 2.08 R (ohne den Punkt 0 R wären es 1.08 R — diese Unterscheidung begründet die Definition in Anhang F). Die Ausgabe gilt für genau diese Datei und die genannten Versionen; bei Abweichung der Reihe nach Daten (Trennzeichen, Dezimalzeichen, Kodierung, Zeilenreihenfolge), S1-Konstanten, Code und Paketversionen prüfen.

S4 · ATR-Positionsgrößenrechner

Python 3 · Standardbibliothek

Volumen in Lots für einen Stop-Abstand von k × ATR: Risikobetrag aus Kapital und Prozentsatz, Abstand aus ATR; Volumen wird auf den Lotschritt Ihres Kontos ABGERUNDET (Parameter lot_step und min_lot , beide standardmäßig 0.01), damit GEPLANTES Risiko das Budget nicht übersteigt. Der tatsächliche Verlust kann größer sein: Slippage, Sprünge durch den Stop, Spread, Provision und Swap fehlen in dieser Rechnung. Kapital und Pip-Wert müssen dieselbe Kontowährung nutzen; Maximalvolumen und Marginanforderung separat in der Spezifikation prüfen. Die ATR in Pips eingeben (EURUSD 0.0080 → 80; USDJPY 0.80 → 80; Pip-Größe im Paarprofil, Anhang B); Pip-Wert für 1 Lot steht in der Instrumentspezifikation oder lässt sich mit dem Rechner in Anhang C berechnen.

import math
from decimal import Decimal
from fractions import Fraction

def atr_position_size(capital, risk_pct, atr_pips, atr_mult, pip_value_per_lot,
                      lot_step=0.01, min_lot=0.01):
    "Volumen in Lots für einen Stop von atr_mult × ATR.\n    atr_pips: ATR in PIPS, nicht Preiseinheiten (EURUSD ATR 0.0080 -> 80; USDJPY ATR 0.80 -> 80 — Pip-Größe des Paars).\n    pip_value_per_lot: Wert von 1 Pip für 1.00 Lot in Kontowährung\n    (z. B. ~10 USD für EURUSD auf USD-Konto — Instrumentspezifikation prüfen).\n    lot_step / min_lot: Volumenschritt und Mindestvolumen IHRES Kontos (0.01 ist üblich, nicht allgemeingültig).\n    Rundet auf Vielfache von lot_step ab null AB. Bruchrechnung\n    erhält Dezimaleingaben unabhängig von der globalen Decimal-Genauigkeit.\n    Geplantes Risiko ist nicht Maximalverlust: Slippage, Gaps, Spread und Swap fehlen im Modell."
    inputs = (capital, risk_pct, atr_pips, atr_mult, pip_value_per_lot, lot_step, min_lot)
    if any(isinstance(x, bool) or not isinstance(x, (int, float, Decimal)) for x in inputs):
        raise ValueError("Numerische Werte verwenden, weder Text noch boolesche Werte (Python-Dezimalzahlen nutzen einen Punkt).")
    decimals = tuple(Decimal(str(x)) for x in inputs)
    if not all(x.is_finite() and x > 0 for x in decimals):
        raise ValueError("Alle Eingaben müssen endliche positive Zahlen sein.")
    c, p, a, k, v, step, minimum = map(Fraction, decimals)
    if p > 100:
        raise ValueError("risk_pct ist ein Kapitalprozentsatz (1.0 = 1%).")
    risk_amount = c * p / 100
    sl_pips     = a * k
    lots        = math.floor(risk_amount / (sl_pips * v * step)) * step
    if lots < minimum:
        return {"warning": "Für lot_step und min_lot ist innerhalb des Risikobudgets kein zulässiges Volumen möglich."}
    exact = {"risk_amount": risk_amount, "sl_pips": sl_pips,
             "lots": lots, "planned_risk": lots * sl_pips * v}
    try:
        result = {key: float(value) for key, value in exact.items()}
    except OverflowError:
        raise ValueError("Eingaben überschreiten den numerischen Ausgabebereich.") from None
    if not all(math.isfinite(value) and value > 0 for value in result.values()):
        raise ValueError("Eingaben überschreiten den numerischen Ausgabebereich.")
    return result  # Gleitkommazahlen zur Anzeige; keine zusätzliche Rundung von Stop oder Geldbeträgen


print(atr_position_size(capital=10_000, risk_pct=1.0,
                        atr_pips=80, atr_mult=1.5, pip_value_per_lot=10.0))
# {'risk_amount': 100.0, 'sl_pips': 120.0, 'lots': 0.08, 'planned_risk': 96.0}

Sie erhalten: auf den Schritt abgerundete Lots und den daraus folgenden GEPLANTEN Risikobetrag (im Modell ohne Kosten ≤ Budget; tatsächlicher Verlust kann größer sein). Der Schritt wird ab null gemessen; prüfen, ob Ihr Konto dieses Volumenraster nutzt. Stop und Geldbeträge werden nicht weiter gerundet: Zum Beispiel ergeben ATR von 0.26 Pips × 1.25 genau 0.325 Pips, nicht 0.3. Die lesbaren Ausgabewerte sind vom Typ float , während die Anzahl der Lotschritte mit exakten Brüchen berechnet wird; die minimale Preisänderung des Instruments bleibt separat zu prüfen. Passen Sie den Stop daran an, das Volumen neu berechnen. Keine Einstiegsentscheidung erwarten — der Rechner beantwortet „wie viel“, niemals „ob“.

S5 · Tagesspanne im Verhältnis zur ATR mit Alarm

Pine Script v6 · Indikator

Werkzeug für Volatilitätsbewusstsein (Intraday-Indikator): Zeigt auf laufenden Kerzen, welchen Prozentsatz der Tages-ATR der Markt bereits „verbraucht“ hat, und meldet Schwellenüberschreitungen — Anlass zum Planabgleich, nicht zum Hinterherjagen. Tagesdaten werden ohne Lookahead (ohne Zukunftswerte) abgefragt; auf historischen Kerzen hält request.security den letzten bestätigten Tageswert bis zum Ende der nächsten D-Periode; deren neues Ergebnis erscheint auf der letzten Intraday-Kerze. In Echtzeit ruft die Funktion die entstehende Spanne und ATR der aktuellen D-Kerze ab, sodass frühere Werte nach Neuladen anders aussehen können (Repainting). Ein Tag ist die D-Kerze des Datenanbieters, kein Kalendertag in Europe/Warsaw. Auch die ATR im Nenner ändert sich tagsüber; 100% begrenzt mögliche Kursbewegung nicht. Diese Methode kann auf historischen Kerzen die aufeinanderfolgenden Schwellenüberschreitungen des heutigen Tages nicht rekonstruieren.

//@version=6
indicator("Tagesspanne im Verhältnis zur ATR (Orientierungswerkzeug)", overlay=false)

if not timeframe.isintraday
    runtime.error("Zeitbasierten Intraday-Chart unter 1D verwenden.")

atrLen  = input.int(14, "ATR-Länge", minval=2)
warnPct = input.int(80, "Alarm, wenn Tagesspanne % der ATR erreicht", minval=10, maxval=200)

// Historisch: letzter bestätigter D-Wert, Aktualisierung am ENDE jeder D-Periode.
// Echtzeit: entstehende D-Werte; vergangene Echtzeitwerte können nach Neuladen neu gezeichnet werden.
dayHigh = request.security(syminfo.tickerid, "D", high,            lookahead=barmerge.lookahead_off)
dayLow  = request.security(syminfo.tickerid, "D", low,             lookahead=barmerge.lookahead_off)
dayAtr  = request.security(syminfo.tickerid, "D", ta.atr(atrLen),  lookahead=barmerge.lookahead_off)

rangePct = dayAtr > 0 ? 100.0 * (dayHigh - dayLow) / dayAtr : na

plot(rangePct, "Tagesspanne als % der ATR", linewidth=2)
hline(100, "100% der ATR")
hline(warnPct, "Alarmschwelle", linestyle=hline.style_dotted)

alertcondition(ta.crossover(rangePct, warnPct),
     title="Spanne erreicht % der ATR",
     message="Tagesspanne hat Ihren ATR-Prozentsatz erreicht — vor jedem Neueinstieg den Plan erneut lesen.")

Sie erhalten: einen Bereich unter dem Chart und eine Alarmbedingung — Alarm nach Hinzufügen des Indikators selbst in der Plattform anlegen und „Spanne erreicht % der ATR“ auswählen. Standardmäßige zeitbasierte Intraday-Kerzen nutzen; Heikin-Ashi-/Renko-Daten können andere Eigenschaften haben. Ein fehlender Wert (na) bedeutet fehlende erforderliche ATR oder Daten, nicht 0%. Die Bedingung verlangt einen Wechsel von einem Wert ≤ Schwelle zu einem Wert > Schwelle: Bloßes Verbleiben darüber löst keinen weiteren Alarm aus. „Once Per Bar Close“ beschränkt Alarme auf den Intraday-Kerzenschluss, bestätigt aber nicht die gesamte D-Kerze. Nach Parameteränderung den Alarm neu erstellen. Kein Richtungssignal erwarten — ein hoher Wert sagt „die Bewegung ist schon passiert“; das Playbook bestimmt den Umgang damit.

S6 · Handelsplan-Zeitfenster im Chart

Pine Script v6 · Indikator

Abschnitt 3 des Plans (Anhang E) im Chart: Markiert Zeiten, in denen Sie Positionen eröffnen DÜRFEN, und meldet das Fensterende. Zeiten in der Zeitzone Europe/Warsaw eingeben — der IANA-Bezeichner berücksichtigt MEZ/MESZ. Standardmäßig gelten die Fenster Montag bis Freitag; eine Option erweitert sie auf alle Tage. Die Markierung bestätigt nur die Planzeit, keine anderen Einstiegsbedingungen. Standardmäßige zeitbasierte Intraday-Kerzen nutzen.

//@version=6
indicator("Handelsplan-Zeitfenster", overlay=true)

if not timeframe.isintraday
    runtime.error("Zeitbasierten Intraday-Chart unter 1D verwenden.")

tz   = input.string("Europe/Warsaw", "Zeitzone der Fenster")
win1 = input.session("0900-1200", "Fenster 1")
win2 = input.session("1400-1700", "Fenster 2")

weekdaysOnly = input.bool(true, "Nur Montag bis Freitag")
days = weekdaysOnly ? ":23456" : ":1234567"

inWin = not na(time(timeframe.period, win1 + days, tz)) or not na(time(timeframe.period, win2 + days, tz))

bgcolor(inWin ? color.new(color.teal, 88) : na, title="Innerhalb eines Planfensters")

windowJustClosed = inWin[1] and not inWin
alertcondition(windowJustClosed,
     title="Planfenster geschlossen",
     message="Ihr Handelsfenster ist gerade zu Ende gegangen — nur offene Positionen verwalten, keine Neueinstiege (Plan, Abschnitt 3).")

Sie erhalten: Hintergrundmarkierung während der Handelszeiten und eine Alarmbedingung für das Fensterende („Planfenster geschlossen“ — nach Hinzufügen des Indikators den Alarm in der Plattform erstellen). Erkannt wird die erste Kerze außerhalb des Fensters nach einer Kerze innerhalb. Das garantiert keine Meldung genau zur Schlusszeit: Ohne neue Kerze und Datenaktualisierung erfolgt keine Prüfung. Einen Zeitrahmen mit zu den Fenstergrenzen passenden Eröffnungen und Schlüssen nutzen. Beispiel: M15, Fenster 09:00–12:00 und verfügbare 12:00-Kerze — die Markierung endet 12:00, die Bedingung ist auf dieser ersten Kerze außerhalb aktiv. „Once Per Bar“ kann bei deren erster Aktualisierung melden; „Once Per Bar Close“ erst bei ihrem Schluss. Überlappende Fenster bilden eine Vereinigung: Das Ende des ersten schließt das zweite nicht. Nach Zeitänderungen den Alarm neu erstellen. Keine Ordersperre erwarten — der Indikator erinnert an den Plan; Ihre Pre-Trade-Checkliste setzt ihn durch.

Pine-Script-v6-Indikatoren auf einem TradingView-EUR/USD-Chart: markierte Handelsplan-Zeitfenster (S6) und unten Tagesspanne/ATR-Bereich mit 80%-Alarmschwelle (S5)
Pine-Script-v6-Indikatoren auf einem TradingView-EUR/USD-Chart: markierte Handelsplan-Zeitfenster (S6) und unten Tagesspanne/ATR-Bereich mit 80%-Alarmschwelle (S5)
Gesamtes Paket in einer Datei lokal in Ihrem Browser erzeugt

Sechs Codebeispiele mit Überschriften in einer Textdatei, Einrichtungsanleitung und Bibliotheksversionen, danach Test-CSV und erwartete Ausgabe — zur Archivierung beim Journal oder im eigenen Repository. Die TXT kombiniert verschiedene Sprachen: Nicht als Ganzes als Python ausführen. S1+S2+S3, das eigenständige S4 oder einen einzelnen Pine-Indikator auswählen.

Tatsächliche S3-Ausgabe für fünf Testtrades: Codeeditor mit Python-Skript und Terminal-Kennzahlen neben der erzeugten Ergebniskurve und dem Drawdown-Chart
Tatsächliche S3-Ausgabe für fünf Testtrades: Codeeditor mit Python-Skript und Terminal-Kennzahlen neben der erzeugten Ergebniskurve und dem Drawdown-Chart

FAQ

Welche Bestandteile des Codes bleiben englisch?

API-Namen und ausführbare Bezeichner bleiben in diesem Paket englisch und entsprechen der Dokumentation von pandas und Pine Script sowie den üblicherweise englischen Fehlermeldungen. Die deutsche Fassung übersetzt menschliche Kommentare und Ausgabebezeichnungen. Die Erläuterungen um den Code erklären, WAS er macht und wann er anzuwenden ist; Kommentare erklären WIE. In der ursprünglichen polnischen Kursfassung blieben Kommentare dieser Bibliothek englisch; die dortige P5-Ausnahme mit polnischen Kommentaren galt für eigens erzeugten Einmal-Code. Die KI-Werkstatt berücksichtigt bei Code-Prompts dieselbe Trennung (Anhang H, P3).

Wie führe ich die Python-Beispiele aus?

11.8 führt Schritt für Schritt durch Python-, Umgebungs- und Paketinstallation — pandas und matplotlib genügen für dieses Paket. S1–S3 in einer Datei analiza_dziennika.py kombinieren (S1 oben) und python analiza_dziennika.py im Verzeichnis des Journalexports ausführen; S4 läuft eigenständig. Der relative Pfad CSV_PATH bezieht sich auf das Arbeitsverzeichnis des Terminals. Mit der Test-CSV beginnen und eine Kopie des echten Journals analysieren; equity_curve.png wird beim nächsten Lauf überschrieben. Bei einer unverstandenen Terminalfehlermeldung Code und vollständige Meldung in eine P3-Prüfung aus Anhang H einfügen, statt zu raten.

Kann ich diese Beispiele ändern und erweitern?

Dafür sind sie da — dies ist ein STARTERPAKET. Naheliegende Erweiterungen sind Kennzahlen je Setup (vor dem Aufruf der S2-Funktion nach Setup-Spalte gruppieren), Filtern nach error_code für den Erwartungswert sauber ausgeführter Trades oder separate Kurven für A+/B-Klassen. Zwei Änderungsregeln: Jede Änderung durch eine P3-Prüfung schicken (besonders alles mit request.security in Pine) und Kennzahldefinitionen wie Strategieregeln versionieren. Rechenfehler sofort beheben und Vergleichsperioden mit derselben Methode neu berechnen; Änderungen von Strategieregeln im Testprotokoll (Anhang N) dokumentieren und auf neuen Daten prüfen. Ein Quartalsende rechtfertigt allein keine Änderung; ein laufendes Quartal rechtfertigt keinen beibehaltenen Fehler.

Warum enthält das Paket keine Strategien oder EAs?

Aus demselben Grund, aus dem die KI-Werkstatt keine Signale erzeugt: Eine fertige Internetstrategie besitzt einen unbekannten Vorteil, unbekannte Annahmen und keinen Bezug zu Ihrem Plan. Ein EA, der fremde Logik auf Ihrem Konto ausführt, delegiert Entscheidungen, für die allein Sie bezahlen. Kurscode stärkt den Prozess (Messen, Visualisieren und Planeinhaltung), statt ihn zu ersetzen. Wollen Sie Ihre eigene getestete Strategie automatisieren, führt der Weg über den Backtest in 15.4, das Testprotokoll in Anhang N und eine P3-Codeprüfung, nicht über das Kopieren „profitabler“ Skripte.

Illustratives Szenario: Runden „auf eine schönere Zahl“. Der Rechner ermittelte 0.07 Lots; der Trader klickte 0.1 — „nur kosmetisch, eine runde Zahl“. Diese kosmetische Änderung in Zahlen: +43% Risiko bei jedem Trade, systematisch das ganze Quartal; das Tageslimit von −2R entsprach effektiv −2.9R, und der Drawdown der Ergebniskurve überschritt den Plan, obwohl der Trader jede Änderung für kosmetisch hielt. Deshalb math.floor in S4 statt round: Die Maschine rundet ohne Diskussion ab; der Mensch rundet mit Begründung auf.
Illustratives Szenario: Verlusthäufung nur in der Kurve sichtbar. Die Quartalskennzahlen wirkten vernünftig: positiver Erwartungswert, Drawdown im erwarteten Bereich. Erst der S3-Chart zeigte, dass der gesamte Drawdown aus einer einzigen Woche kam. Ein Einstiegszeitfilter in S2 präzisierte: jeder Verlust zwischen 21:00 und 23:00 UTC (das Journal in Anhang F erfasst UTC), bei breiten Spreads, „ich bleibe noch etwas“. Ob diese Zeiten mit dem Rollover zusammenfallen, hängt von Broker und Zeitzone ab — Spezifikation prüfen statt annehmen. Eine Gesamtzahl mittelt; die Kurve zeigt, WANN das System blutet. Eine Planzeile (keine neuen Einstiege nach 21:00 UTC beziehungsweise 23:00 MESZ / 22:00 MEZ — im Plan und den S6-Fenstern als Europe/Warsaw-Zeit notieren) ist in diesem Szenario eine über die folgenden Wochen zu testende Schlussfolgerung, keine fertige Ergebnisverbesserung — genau solche Schlussfolgerungen soll das Paket unterstützen.
Hinweis: Diese Seite dient der Bildung und ist keine Anlageberatung. Die Codebeispiele sind Analysewerkzeuge auf eigene Verantwortung: Vor Nutzung lesen, Ergebnisse prüfen (etwa mit dem Rechner aus Anhang C) und Handelsentscheidungen nach eigenem Plan treffen. Die Python-Beispiele S1–S4 verbinden sich weder mit Internet noch Konto — sie lesen lokale Dateien und zeichnen Charts; die Pine-Indikatoren S5–S6 laufen auf Ihrer Chartplattform mit deren Daten. Kein Beispiel erzeugt Kauf-/Verkaufssignale oder erteilt Orders.

Quellen

  1. Dokumentation: pandas 3.0.5 und matplotlib 3.11.1 (in S1–S3 verwendete Funktionen); Python: CSV einlesen und Bruchrechnung (Formatvalidierung und exakte Berechnung der Lotschrittzahl) — S1–S4 wurden am 17. September 2026 mit Python 3.12.10 auf synthetischen Daten ausgeführt; TradingView Pine Script v6: andere Zeitrahmen und Daten, Sitzungen, Zeit und Zeitzonen, Alarme (request.security mit Lookahead-Parameter und Repainting, hline, input.session, alertcondition) — S5–S6 anhand der Dokumentation geprüft, ohne Kompilierung.
  2. Intern: 11.8 Python im Trading (Umgebung), 15.9 (Kennzahldefinitionen und Journalformat), 15.10 (Grundsätze der Schlussfolgerung) — dieses Paket ist deren praktische Kurzimplementierung.
  3. Van K. Tharp, Trade Your Way to Financial Freedom, 2. Auflage, McGraw-Hill 2007 — R-Multiples und Erwartungswert als gemeinsame Sprache von S2, Arbeitsblatt F und Prompt P5.

Jarosław Wasiński

Chefredakteur, ForexMechanics.com • Makro- und FX-Analyst

Unabhaengiger Analyst und Finanzmarkt-Experte mit ueber 20 Jahren Erfahrung an den Finanzmaerkten. Aktiv im Forex-Handel seit 2004, mit Schwerpunkt auf Fundamentalanalyse, der Struktur des OTC-Marktes und disziplinierter Risikosteuerung des Handelskapitals.

  • Gruender und Chefredakteur von MyBank.pl, der seit 2004 verlaessliche Finanzbildung anbietet.
  • Autor hunderter tiefgehender Marktkommentare, struktureller Analysen und Bildungsmaterialien fuer Trader und Investoren.
  • Verfechter der Markttransparenz. Er foerdert eine Ausbildung, die auf belastbaren Daten und institutionellen Berichten (BIS, ESMA, CFTC) basiert.

Alle Inhalte dienen ausschliesslich Bildungs- und Informationszwecken – sie stellen weder Anlageberatung noch eine Empfehlung dar. Bitte beachten Sie: Der gehebelte Handel (Forex/CFD) birgt ein hohes Risiko eines schnellen Kapitalverlusts.