Initial commit: Oil Trading Bot (MT5, WTI)
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/), Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Momentum-Continuation als AUTONOMER Setup-Kandidat (2026-07-17): Einstieg in
|
||||
Richtung eines starken, frischen Momentum-Schubs (Kurs ≥ X×ATR über N Bars gelaufen)
|
||||
— Trend-Persistenz-Wette, andere Klasse als der Squeeze (Kompression→Ausbruch).
|
||||
|
||||
Sequentielle 1-Positions-Sim (wie live), Exit = SL 2,0×ATR + Trailing 1,5 + BE 1,3.
|
||||
Entry = FRISCHER Schub (mom_N kreuzt X, war die N Bars davor drunter → kein Einstieg
|
||||
tief in einen ausgelaufenen Move). Kosten = Bar-Spread/ATR. 2 Halbjahre.
|
||||
Robustheit über N (6/12/24) × X (1,0/1,5/2,0).
|
||||
|
||||
Automatisieren nur, wenn eine Kombi ØR & PF in BEIDEN Hälften klar positiv hält
|
||||
(Maßstab: Squeeze ØR +0,14…+0,23).
|
||||
"""
|
||||
import sys
|
||||
import MetaTrader5 as mt5
|
||||
_MAXH = 288; _ATRMIN = 0.12; _COOL = 6
|
||||
|
||||
|
||||
def _atr_series(H, L, C, p=14):
|
||||
t = [0.0]
|
||||
for i in range(1, len(C)):
|
||||
t.append(max(H[i]-L[i], abs(H[i]-C[i-1]), abs(L[i]-C[i-1])))
|
||||
return [(sum(t[max(1, i-p+1):i+1])/max(1, len(t[max(1, i-p+1):i+1]))) if i else None
|
||||
for i in range(len(C))]
|
||||
|
||||
|
||||
def st(Rs):
|
||||
if not Rs: return None
|
||||
n = len(Rs); w = sum(1 for x in Rs if x > 0); s = sum(Rs)
|
||||
up = sum(x for x in Rs if x > 0); dn = -sum(x for x in Rs if x < 0)
|
||||
return dict(n=n, wr=100*w/n, oR=s/n, pf=(up/dn if dn > 0 else 9.99), sum=s)
|
||||
|
||||
|
||||
def run(H, L, C, A, SP, lo, hi, N, X):
|
||||
def cost(i, atr): return (SP[i] if SP[i] > 0 else 0.0225)/atr
|
||||
Rs = []; i = max(lo, N+1)
|
||||
while i < hi:
|
||||
atr = A[i]
|
||||
if not atr or atr < _ATRMIN or i-N < 0:
|
||||
i += 1; continue
|
||||
mom = (C[i]-C[i-N])/atr
|
||||
momP = (C[i-1]-C[i-1-N])/atr if i-1-N >= 0 else 0.0
|
||||
# frischer Schub: jetzt |mom|≥X, letzten Bar noch drunter
|
||||
d = 0
|
||||
if mom >= X and momP < X: d = 1
|
||||
elif mom <= -X and momP > -X: d = -1
|
||||
if d == 0:
|
||||
i += 1; continue
|
||||
atr = max(atr, _ATRMIN); entry = C[i]
|
||||
eff = entry - d*2.0*atr; hw = entry
|
||||
end = min(i+_MAXH, len(C)-1); exit_px = C[end]; exit_j = end
|
||||
for j in range(i+1, end+1):
|
||||
hj, lj = H[j], L[j]
|
||||
if (lj <= eff) if d > 0 else (hj >= eff): exit_px = eff; exit_j = j; break
|
||||
hw = max(hw, hj) if d > 0 else min(hw, lj)
|
||||
prof = (C[j]-entry)*d
|
||||
if prof >= 0.3*atr:
|
||||
cand = hw - d*1.5*atr
|
||||
if prof >= 1.3*atr: cand = max(cand, entry) if d > 0 else min(cand, entry)
|
||||
eff = max(eff, cand) if d > 0 else min(eff, cand)
|
||||
Rs.append((exit_px-entry)*d/atr - cost(i, atr))
|
||||
i = exit_j + _COOL
|
||||
return Rs
|
||||
|
||||
|
||||
def main():
|
||||
n = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
|
||||
mt5.initialize()
|
||||
sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD") if mt5.symbol_info(c)), None)
|
||||
bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n+_MAXH+30)
|
||||
point = mt5.symbol_info(sym).point; mt5.shutdown()
|
||||
H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars]
|
||||
C = [float(b["close"]) for b in bars]; SP = [float(b["spread"])*point for b in bars]
|
||||
A = _atr_series(H, L, C); N_ = len(C); mid = N_//2
|
||||
|
||||
print("="*84)
|
||||
print(f" Momentum-Continuation — {sym} M5 (seq. Sim · Exit SL2/Trail1,5/BE1,3 · Echtkosten)")
|
||||
print(f" Entry = frischer Momentum-Schub (mom_N kreuzt ±X×ATR). 2 Halbjahre.")
|
||||
print("="*84)
|
||||
print(f" {'N/X':>8} | {'H1: n ØR PF':>22} | {'H2: n ØR PF':>22}")
|
||||
for N in (6, 12, 24):
|
||||
for X in (1.0, 1.5, 2.0):
|
||||
s1 = st(run(H, L, C, A, SP, 0, mid, N, X))
|
||||
s2 = st(run(H, L, C, A, SP, mid, N_, N, X))
|
||||
def f(s): return f"{s['n']:>4} {s['oR']:+.3f} {s['pf']:>5.2f}" if s else " – "
|
||||
ok = "OK" if (s1 and s2 and s1['oR'] > 0 and s2['oR'] > 0
|
||||
and s1['pf'] > 1 and s2['pf'] > 1) else ""
|
||||
print(f" N={N:>2} X={X:.1f} | {f(s1):>22} | {f(s2):>22} {ok}")
|
||||
print(f"\n Automatisieren nur bei ØR>0 & PF>1 in BEIDEN Hälften (Squeeze-Maßstab +0,14…+0,23).")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user