signal_aggregator + chart_patterns: drop dead parameters and shadowed literals (no behaviour change)
- analyze_pair(context=...): no caller ever supplied it (main, telegram_bot, pair_signals, tests), so the value was always None; the parameter and the pass-through to analyze_from_dataframes go (analyze_from_dataframes keeps its own context parameter). - The three specific "SELL FILTER: quality|volatility|regime" stale-prefix entries were shadowed by the general "SELL FILTER: " entry in the same startswith() tuple; every emitter carries the general prefix. - chart_patterns: the detectors never read the Open series, so the `o` parameter is dropped from the three detectors, the PATTERN_DETECTORS lambdas and the dispatcher. - Corrected the pb_funnel/cp_funnel comment: nothing reads those counters (the "read by backtest tooling" claim was stale); they stay as cheap diagnostics. Parity: 12-month M5 and H1 books regenerated byte-identical (md5 a28fed4becd056466c26d6a947e1a0e5 / d20405ef64f069770c258bb9bed1ac90). Suite: 461 passed, 0 failed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0156YhogLytzj9P2iH1skGbL
This commit is contained in:
+10
-11
@@ -97,7 +97,7 @@ def _first_close_beyond(closes: np.ndarray, start: int, level: float,
|
|||||||
|
|
||||||
# ── Double bottom / top ──────────────────────────────────────────────
|
# ── Double bottom / top ──────────────────────────────────────────────
|
||||||
|
|
||||||
def _detect_double(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
def _detect_double(h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
||||||
w = cfg.cp_swing_window
|
w = cfg.cp_swing_window
|
||||||
ext = l if bullish else h
|
ext = l if bullish else h
|
||||||
idx = _swing_indices(ext, w, find_high=not bullish)
|
idx = _swing_indices(ext, w, find_high=not bullish)
|
||||||
@@ -147,7 +147,7 @@ def _detect_double(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
|||||||
|
|
||||||
# ── Head & shoulders (top and inverse) ───────────────────────────────
|
# ── Head & shoulders (top and inverse) ───────────────────────────────
|
||||||
|
|
||||||
def _detect_hs(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
def _detect_hs(h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
||||||
"""bullish=True → inverse H&S (three swing LOWS, head deepest)."""
|
"""bullish=True → inverse H&S (three swing LOWS, head deepest)."""
|
||||||
w = cfg.cp_swing_window
|
w = cfg.cp_swing_window
|
||||||
ext = l if bullish else h
|
ext = l if bullish else h
|
||||||
@@ -210,7 +210,7 @@ def _detect_hs(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
|||||||
|
|
||||||
# ── Flags (bull and bear) ────────────────────────────────────────────
|
# ── Flags (bull and bear) ────────────────────────────────────────────
|
||||||
|
|
||||||
def _detect_flag(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
def _detect_flag(h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
||||||
n = len(c)
|
n = len(c)
|
||||||
lo_r, hi_r = cfg.cp_flag_retrace
|
lo_r, hi_r = cfg.cp_flag_retrace
|
||||||
if bullish:
|
if bullish:
|
||||||
@@ -257,12 +257,12 @@ def _detect_flag(o, h, l, c, atr, cfg, bullish: bool) -> Optional[Dict]:
|
|||||||
# Priority order: most structured / rarest first — if two patterns
|
# Priority order: most structured / rarest first — if two patterns
|
||||||
# complete on the same bar, the more specific one names the trade.
|
# complete on the same bar, the more specific one names the trade.
|
||||||
PATTERN_DETECTORS = {
|
PATTERN_DETECTORS = {
|
||||||
"inv_hs": lambda o, h, l, c, atr, cfg: _detect_hs(o, h, l, c, atr, cfg, True),
|
"inv_hs": lambda h, l, c, atr, cfg: _detect_hs(h, l, c, atr, cfg, True),
|
||||||
"hs_top": lambda o, h, l, c, atr, cfg: _detect_hs(o, h, l, c, atr, cfg, False),
|
"hs_top": lambda h, l, c, atr, cfg: _detect_hs(h, l, c, atr, cfg, False),
|
||||||
"double_bottom": lambda o, h, l, c, atr, cfg: _detect_double(o, h, l, c, atr, cfg, True),
|
"double_bottom": lambda h, l, c, atr, cfg: _detect_double(h, l, c, atr, cfg, True),
|
||||||
"double_top": lambda o, h, l, c, atr, cfg: _detect_double(o, h, l, c, atr, cfg, False),
|
"double_top": lambda h, l, c, atr, cfg: _detect_double(h, l, c, atr, cfg, False),
|
||||||
"bull_flag": lambda o, h, l, c, atr, cfg: _detect_flag(o, h, l, c, atr, cfg, True),
|
"bull_flag": lambda h, l, c, atr, cfg: _detect_flag(h, l, c, atr, cfg, True),
|
||||||
"bear_flag": lambda o, h, l, c, atr, cfg: _detect_flag(o, h, l, c, atr, cfg, False),
|
"bear_flag": lambda h, l, c, atr, cfg: _detect_flag(h, l, c, atr, cfg, False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -276,7 +276,6 @@ def detect_chart_pattern(df: pd.DataFrame, atr: float, cfg) -> Optional[Dict]:
|
|||||||
if atr <= 0 or len(df) < cfg.cp_lookback + 1:
|
if atr <= 0 or len(df) < cfg.cp_lookback + 1:
|
||||||
return None
|
return None
|
||||||
tail = df.iloc[-(cfg.cp_lookback + 1):]
|
tail = df.iloc[-(cfg.cp_lookback + 1):]
|
||||||
o = tail["Open"].to_numpy(dtype=np.float64)
|
|
||||||
h = tail["High"].to_numpy(dtype=np.float64)
|
h = tail["High"].to_numpy(dtype=np.float64)
|
||||||
l = tail["Low"].to_numpy(dtype=np.float64)
|
l = tail["Low"].to_numpy(dtype=np.float64)
|
||||||
c = tail["Close"].to_numpy(dtype=np.float64)
|
c = tail["Close"].to_numpy(dtype=np.float64)
|
||||||
@@ -284,7 +283,7 @@ def detect_chart_pattern(df: pd.DataFrame, atr: float, cfg) -> Optional[Dict]:
|
|||||||
for name, det in PATTERN_DETECTORS.items():
|
for name, det in PATTERN_DETECTORS.items():
|
||||||
if name not in enabled:
|
if name not in enabled:
|
||||||
continue
|
continue
|
||||||
hit = det(o, h, l, c, atr, cfg)
|
hit = det(h, l, c, atr, cfg)
|
||||||
if hit is not None:
|
if hit is not None:
|
||||||
return hit
|
return hit
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -150,8 +150,9 @@ class SignalReport:
|
|||||||
class SignalAggregator:
|
class SignalAggregator:
|
||||||
"""Master aggregator for live signal analysis."""
|
"""Master aggregator for live signal analysis."""
|
||||||
|
|
||||||
# Diagnostic funnel for the M5 pullback route (shared across
|
# Diagnostic rejection counters for the M5 pullback route (class-level,
|
||||||
# instances; read by backtest tooling, harmless in live use).
|
# shared across instances). Nothing in the repo reads them; they are
|
||||||
|
# cheap and kept for ad-hoc inspection from a REPL.
|
||||||
pb_funnel: Dict[str, int] = {}
|
pb_funnel: Dict[str, int] = {}
|
||||||
# Same, for the chart-pattern route (XAU_CP).
|
# Same, for the chart-pattern route (XAU_CP).
|
||||||
cp_funnel: Dict[str, int] = {}
|
cp_funnel: Dict[str, int] = {}
|
||||||
@@ -172,7 +173,6 @@ class SignalAggregator:
|
|||||||
def analyze_pair(
|
def analyze_pair(
|
||||||
self,
|
self,
|
||||||
symbol: Optional[str] = None,
|
symbol: Optional[str] = None,
|
||||||
context: Optional[FundamentalContext] = None,
|
|
||||||
log_source: Optional[str] = None,
|
log_source: Optional[str] = None,
|
||||||
) -> SignalReport:
|
) -> SignalReport:
|
||||||
"""Live-mode entry: fetch all needed timeframes then delegate.
|
"""Live-mode entry: fetch all needed timeframes then delegate.
|
||||||
@@ -244,7 +244,6 @@ class SignalAggregator:
|
|||||||
report = self.analyze_from_dataframes(
|
report = self.analyze_from_dataframes(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
dfs=dfs,
|
dfs=dfs,
|
||||||
context=context,
|
|
||||||
use_news=True,
|
use_news=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -919,9 +918,6 @@ class SignalAggregator:
|
|||||||
"EXHAUSTION BLOCK",
|
"EXHAUSTION BLOCK",
|
||||||
"EXHAUSTED TREND FILTER:",
|
"EXHAUSTED TREND FILTER:",
|
||||||
"MTF OPPOSITION:",
|
"MTF OPPOSITION:",
|
||||||
"SELL FILTER: quality",
|
|
||||||
"SELL FILTER: volatility",
|
|
||||||
"SELL FILTER: regime",
|
|
||||||
"SELL FILTER: ",
|
"SELL FILTER: ",
|
||||||
"M5 FILTER: |score|",
|
"M5 FILTER: |score|",
|
||||||
"M5 FILTER: no MTF agreement",
|
"M5 FILTER: no MTF agreement",
|
||||||
|
|||||||
Reference in New Issue
Block a user