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>
77 lines
3.6 KiB
Python
77 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Verlässlichkeit der Bounce-Anzeige ('aktiv'): Wenn _bounce_one(TF)=active feuert
|
||
(überdehnt + Winkel gedreht), wie oft bewegt sich der Kurs in Bounce-Richtung?
|
||
Aufgeschlüsselt nach TF (M1/M5/M15/M30) und nach H1-Regime (mit/gegen/flach zur
|
||
Bounce-Richtung). Nur ONSET (Übergang nicht-aktiv→aktiv) = unabhängige Ereignisse.
|
||
"""
|
||
import sys, bisect
|
||
import MetaTrader5 as mt5
|
||
from core.wave_rec import WaveRecommender, _atr, _ema_last, _EMA_FAST, _EMA_SLOW, _HTF_DEADBAND
|
||
K = 10 # Vorlauf in Bars der jeweiligen TF
|
||
|
||
def _ema_series(v,p):
|
||
k=2.0/(p+1); o=[]; e=v[0]
|
||
for i,x in enumerate(v): e=x if i==0 else x*k+e*(1-k); o.append(e)
|
||
return o
|
||
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 main():
|
||
n=int(sys.argv[1]) if len(sys.argv)>1 else 40000
|
||
mt5.initialize()
|
||
sym=None
|
||
for c in ("SpotCrude","USOIL","WTI","XTIUSD"):
|
||
if mt5.symbol_info(c): sym=c; break
|
||
# H1-Regime vorbereiten
|
||
h1=mt5.copy_rates_from_pos(sym,mt5.TIMEFRAME_H1,0,20000)
|
||
hT=[int(b["time"]) for b in h1]; hc=[float(b["close"]) for b in h1]
|
||
hh=[float(b["high"]) for b in h1]; hl=[float(b["low"]) for b in h1]
|
||
hEf=_ema_series(hc,_EMA_FAST); hEs=_ema_series(hc,_EMA_SLOW); hA=_atr_series(hh,hl,hc)
|
||
def h1_sign(ts):
|
||
idx=bisect.bisect_right(hT,ts)-1
|
||
if idx<_EMA_SLOW or hA[idx] is None or hA[idx]<=0: return 0
|
||
d=hEf[idx]-hEs[idx]
|
||
return 0 if abs(d)<_HTF_DEADBAND*hA[idx] else (1 if d>0 else -1)
|
||
|
||
tfs=[("M1",mt5.TIMEFRAME_M1),("M5",mt5.TIMEFRAME_M5),
|
||
("M15",mt5.TIMEFRAME_M15),("M30",mt5.TIMEFRAME_M30)]
|
||
print("="*86)
|
||
print(f" Bounce-Anzeige 'aktiv' — Verlässlichkeit je TF & H1-Regime ({sym}, Vorlauf {K} Bars)")
|
||
print(" Treffer = Kurs bewegt sich in Bounce-Richtung · Ø = Ø-Bewegung in ATR")
|
||
print("="*86)
|
||
for lbl,tf in tfs:
|
||
r=mt5.copy_rates_from_pos(sym,tf,0,n)
|
||
if r is None or len(r)<200: print(f"{lbl}: zu wenig Bars"); continue
|
||
T=[int(b["time"]) for b in r]; H=[float(b["high"]) for b in r]
|
||
L=[float(b["low"]) for b in r]; C=[float(b["close"]) for b in r]
|
||
buckets={"mit":[], "gegen":[], "flach":[], "alle":[]}
|
||
prev=False
|
||
for i in range(120, len(C)-K):
|
||
wc=C[i-119:i+1]; wh=H[i-119:i+1]; wl=L[i-119:i+1]
|
||
atr=_atr(wh,wl,wc)
|
||
if not atr or atr<=0: prev=False; continue
|
||
b=WaveRecommender._bounce_one(wc,atr)
|
||
active = bool(b and b["state"]=="active")
|
||
if active and not prev:
|
||
d=1 if b["dir"]=="LONG" else -1
|
||
fwd=(C[i+K]-C[i])*d/atr # in ATR, Bounce-Richtung
|
||
reg=h1_sign(T[i])
|
||
cls = "mit" if reg==d else "gegen" if reg==-d else "flach"
|
||
buckets[cls].append(fwd); buckets["alle"].append(fwd)
|
||
prev=active
|
||
print(f"\n{lbl} ({len(buckets['alle'])} aktive Bounce-Signale)")
|
||
for cls in ("alle","mit","gegen","flach"):
|
||
v=buckets[cls]
|
||
if not v: print(f" {cls:<6} -"); continue
|
||
hit=100*sum(1 for x in v if x>0)/len(v)
|
||
print(f" {cls:<6} n={len(v):>4} Treffer={hit:>3.0f}% Ø={sum(v)/len(v):+.2f}×ATR")
|
||
mt5.shutdown()
|
||
print("\n 'mit' = H1-Trend in Bounce-Richtung (Trend-Fortsetzung)")
|
||
print(" 'gegen' = H1-Trend GEGEN den Bounce (Gegen-Trend-Fade)")
|
||
print(" 'flach' = H1 neutral (reine Range/Mean-Reversion)")
|
||
|
||
if __name__=="__main__":
|
||
main()
|