Files
AH-Oil-Trader/analyze_verhalten.py
Axel HocksandClaude Opus 5 496d65aab5 Vier weitere ebooks ausgewertet: drei ohne Ertrag, das vierte widerlegt eine Projekt-Regel
Sechs PDFs in ebooks/, zwei davon heute schon ausgewertet. Die vier neuen als
Text extrahiert (346k Woerter) und gegen das Gemessene gescannt statt gelesen.

- 'Day-Trading Attention' ist KEIN Trading-Buch: Vaynerchuks Social-Media-
  Marketing-Titel, ein Wortspiel. Null TA-Begriffe im ganzen Text.
- 'Die Kunst des technischen und algorithmischen Tradings' (656 S.): RSI/MACD/
  ADX 165x, Elliott 35x, Kerzen 37x, Fibonacci 18x, Bollinger 17x -- alles hier
  bereits gemessen und verworfen. Keine pruefbare Regel, kein CRV-Kapitel.
- 'KDP968 4 in 1': ausdruecklich fuer Einsteiger, unter dem Projektstand.
- 'Ein Fuchs namens Risiko': das einzige mit pruefbaren Aussagen, weil seine
  Kapitel ueber REIHENFOLGE und ABSTAND von Trades sprechen.

Gemessen auf 1.318 eigenen Trades (analyze_verhalten.py, je Lot, Bootstrap-KI):
keine der vier Behauptungen reproduziert. Revenge-Trading und Overtrading zeigen
sogar in die Gegenrichtung (< 5 min nach Verlust -1,53 gegen > 2 h -16,16;
3.-5. Trade -7,23 gegen 11.+ +1,00).

DER EIGENTLICHE BEFUND: die Projekt-eigene Checklisten-Regel 'nach 2 Losses
Session beenden' (seit 30.07. Punkt 6) ist auf den eigenen Trades NICHT
gestuetzt -- keine monotone Verschlechterung, der 3.-Verlust-Bucket ist wieder
positiv, alle vier KI enthalten die Null. Uebernommen aus der Literatur, nie
geprueft.

Nicht ueberinterpretieren: ~20 Zellen getestet, ~1 Fehlalarm erwartbar, zwei
schliessen die Null aus. Beobachtungsdaten, keine Zuweisung. Nichts gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 13:15:49 +02:00

147 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Die Verhaltens-Behauptungen aus „Ein Fuchs namens Risiko“ auf den EIGENEN
Trades gemessen (User-Auftrag 2026-08-19: "nutze alle Buecher als Ideenquelle").
WARUM AUSGERECHNET DIESES BUCH. Von den sechs Buechern im Verzeichnis ist es das
einzige, dessen Kernaussagen auf der vorhandenen Datenlage **pruefbar** sind:
Kap. 9 "Der Drang, Verluste zurueckzuholen" -> Revenge-Trading
Kap. 11 "Overtrading fuehlt sich an wie Kontrolle"
Kap. 12 "Revenge Trading und der rote Nebel"
Kap. 14 "Warum Langeweile profitabel ist"
Kap. 10 "Warum kleine Verluste gesund sind"
Alle vier sind Aussagen ueber die REIHENFOLGE und den ABSTAND von Trades -- und
genau das steht in `trades` (entry_time, exit_time, pnl, lots).
⚠⚠ WARUM DAS NICHT NUR EIN BUCH-TEST IST. Die Projekt-Zahlen zeigen seit
Wochen in dieselbe Richtung: der MENSCH schlaegt den Bot (+3,20 gegen
-5,85 EUR/Lot), 28 Signal-Eingriffe sind gescheitert, und die einzige
beidhaelftig robuste Aussage ist ein VETO (gegen das Signal -6,60 EUR/Lot).
Der Hebel liegt gemessen im Verhalten -- nur ist das Verhalten selbst hier nie
gemessen worden. Die Entry-Checkliste nennt "nach 2 Losses Session beenden" als
Regel, ohne sie je auf den eigenen Trades geprueft zu haben.
⚠ JE LOT NORMIERT. Absolute Euro sind bei schwankender Positionsgroesse
ungueltig (Lehre 04.08.: der 04.08. sah nur wegen groesserer Lots wie ein
Ausreisser aus). Gerechnet wird `pnl / lots`.
⚠ 95-%-BOOTSTRAP je Zelle. Ein Punktschaetzer ist auf so kleinen Gruppen keine
Aussage. Zellen mit n < 15 werden als zu duenn markiert statt interpretiert.
⚠ GRENZE, vorab benannt: das sind BEOBACHTUNGSdaten, keine Zuweisung. Wer nach
einem Verlust weiterhandelt, waehlt das selbst -- ein Unterschied kann also auch
heissen "in solchen Marktlagen handelt er anders", nicht "der Zeitabstand ist
die Ursache". Die Richtung ist trotzdem verwertbar, weil sie handlungsnah ist.
Aufruf: python analyze_verhalten.py [tage]
"""
import sqlite3
import sys
from backtest_cost_gate import ci95
DB = "oil_widget_history.db"
MIN_N = 15
def lade(tage):
c = sqlite3.connect(DB)
c.row_factory = sqlite3.Row
q = ("SELECT entry_time, exit_time, pnl, lots FROM trades "
" WHERE exit_time IS NOT NULL AND pnl IS NOT NULL AND lots > 0 ")
if tage:
q += " AND entry_time > strftime('%s','now','-{} days') ".format(int(tage))
q += " ORDER BY entry_time"
rows = [dict(r) for r in c.execute(q)]
c.close()
for r in rows:
r["pl"] = r["pnl"] / r["lots"]
return rows
def zeig(lbl, werte):
if not werte:
print(" {:<34} --".format(lbl)); return
n = len(werte); m = sum(werte) / n
ci = ci95(werte) if n >= 20 else None
s = "n={:>4} {:>5.0f}% Gew. {:+8.2f} EUR/Lot".format(
n, 100.0 * sum(1 for x in werte if x > 0) / n, m)
if ci:
s += " [{:+7.2f} .. {:+7.2f}]".format(ci[0], ci[1])
s += "" if ci[0] * ci[1] > 0 else " ~0"
if n < MIN_N:
s += " ⚠ zu duenn"
print(" {:<34} {}".format(lbl, s))
def main():
tage = int(sys.argv[1]) if len(sys.argv) > 1 else 0
tr = lade(tage)
if len(tr) < 50:
print("zu wenige Trades:", len(tr)); return
print("=" * 100)
print(" VERHALTENS-MESSUNG auf {} eigenen Trades{}".format(
len(tr), " (letzte {} Tage)".format(tage) if tage else " (gesamt)"))
print(" je Lot normiert · 95-%-Bootstrap · Zellen < {} als zu duenn markiert".format(MIN_N))
print("=" * 100)
# ---- (1) REVENGE: Abstand zum vorigen EXIT, getrennt nach dessen Ausgang --
print("\n (1) REVENGE-TRADING — wie schnell nach dem letzten Ausstieg?")
print(" " + "-" * 96)
for nach_verlust in (True, False):
gr = {"< 5 min": [], "530 min": [], "30120 min": [], "> 2 h": []}
for a, b in zip(tr, tr[1:]):
if (a["pnl"] < 0) != nach_verlust:
continue
dt = (b["entry_time"] - a["exit_time"]) / 60.0
if dt < 0:
continue
k = ("< 5 min" if dt < 5 else "530 min" if dt < 30
else "30120 min" if dt < 120 else "> 2 h")
gr[k].append(b["pl"])
print(" nach einem {}:".format("VERLUST" if nach_verlust else "Gewinn"))
for k, v in gr.items():
zeig(" " + k, v)
# ---- (2) VERLUST-SERIE ---------------------------------------------------
print("\n (2) VERLUST-SERIE — der wievielte Trade nach wie vielen Verlusten?")
print(" (Projekt-Regel der Entry-Checkliste: 'nach 2 Losses Session beenden')")
print(" " + "-" * 96)
serie = 0
gr = {0: [], 1: [], 2: [], 3: []}
for t in tr:
gr[min(serie, 3)].append(t["pl"])
serie = serie + 1 if t["pnl"] < 0 else 0
for k in sorted(gr):
zeig(" nach {} Verlust{} in Folge".format(k, "" if k == 1 else "en"), gr[k])
# ---- (3) OVERTRADING / "Langeweile" -------------------------------------
print("\n (3) OVERTRADING — der wievielte Trade des Tages?")
print(" " + "-" * 96)
tage_d = {}
for t in tr:
d = int(t["entry_time"]) // 86400
tage_d.setdefault(d, []).append(t)
gr = {"1.2.": [], "3.5.": [], "6.10.": [], "11.+": []}
tagesbilanz = {"13 Trades": [], "48": [], "915": [], "16+": []}
for d, ts in tage_d.items():
for i, t in enumerate(ts, 1):
k = ("1.2." if i <= 2 else "3.5." if i <= 5
else "6.10." if i <= 10 else "11.+")
gr[k].append(t["pl"])
n = len(ts)
kk = ("13 Trades" if n <= 3 else "48" if n <= 8
else "915" if n <= 15 else "16+")
tagesbilanz[kk].append(sum(x["pl"] for x in ts))
for k, v in gr.items():
zeig(" " + k + " Trade des Tages", v)
print("\n (4) „LANGEWEILE IST PROFITABEL“ — TAGESbilanz je Lot nach Handelsmenge")
print(" (Einheit: Summe EUR/Lot des Tages, n = Anzahl Tage)")
print(" " + "-" * 96)
for k, v in tagesbilanz.items():
zeig(" " + k, v)
print("=" * 100)
if __name__ == "__main__":
main()