main: build_config uses the pair factory's confirmation map (fixes an M5 divergence)
main.py kept a private confirmation table and passed it into the pair factory, which honours any non-None list. For M5 that table said [M1, M15] while the factory, telegram_bot.TF_CONFIRMATIONS and dashboard_publisher.CONFIRMATIONS all use the validated [M15, H1] - so `main.py --timeframe M5` scanned with an unvalidated pairing (and an extra M1 fetch) that no other surface used. - build_config now returns PAIR_PRODUCERS[pair](tf, None): the factory map is the single source of truth. The unreachable bare-PlatformConfig fallback is gone here and in telegram_bot._run_signal_sync (every symbol that reaches either is registered). - --timeframe offers the four validated primaries (M5, M15, H1, H4). M30 and D1 were never validated and silently fell through to the generic [M15, H4] map; they are removed from the CLI. - The "all valid timeframes" test now asserts each CLI timeframe's confirmation list equals pair_signals.xauusd._CONFIRMATIONS - the check that would have caught this. CLI behaviour change: M5 confirmations [M1, M15] -> [M15, H1]; M30/D1 rejected. Bot and dashboard scans are unchanged. 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:
@@ -308,7 +308,7 @@ Examples:
|
|||||||
parser.add_argument("--pair", "-p", default="XAUUSD",
|
parser.add_argument("--pair", "-p", default="XAUUSD",
|
||||||
help="Pair to analyze: XAUUSD (default: XAUUSD)")
|
help="Pair to analyze: XAUUSD (default: XAUUSD)")
|
||||||
parser.add_argument("--timeframe", "-tf", default="H1",
|
parser.add_argument("--timeframe", "-tf", default="H1",
|
||||||
choices=["M5", "M15", "M30", "H1", "H4", "D1"],
|
choices=list(_TF_BY_NAME),
|
||||||
help="Primary analysis timeframe (default: H1)")
|
help="Primary analysis timeframe (default: H1)")
|
||||||
parser.add_argument("--detail", action="store_true",
|
parser.add_argument("--detail", action="store_true",
|
||||||
help="Show full indicator breakdown")
|
help="Show full indicator breakdown")
|
||||||
@@ -326,6 +326,14 @@ Examples:
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
# The four validated primary timeframes (the same set the bot and the
|
||||||
|
# dashboard scan). M30/D1 were never validated as primaries.
|
||||||
|
_TF_BY_NAME = {
|
||||||
|
"M5": Timeframe.M5, "M15": Timeframe.M15,
|
||||||
|
"H1": Timeframe.H1, "H4": Timeframe.H4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _resolve_pair(s: str) -> str:
|
def _resolve_pair(s: str) -> str:
|
||||||
aliases = {
|
aliases = {
|
||||||
"XAUUSD": XAUUSD, "XAU/USD": XAUUSD, "GOLD": XAUUSD,
|
"XAUUSD": XAUUSD, "XAU/USD": XAUUSD, "GOLD": XAUUSD,
|
||||||
@@ -337,35 +345,15 @@ def _resolve_pair(s: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def build_config(args) -> PlatformConfig:
|
def build_config(args) -> PlatformConfig:
|
||||||
|
"""Per-pair tuned config from the pair_signals factory.
|
||||||
|
|
||||||
|
The factory owns the validated confirmation map (M5->[M15,H1],
|
||||||
|
M15->[M5,H1], H1->[M15,H4], H4->[H1,D1]); passing None here keeps
|
||||||
|
the CLI on the same map as the bot and the dashboard. A CLI-local
|
||||||
|
table once paired M5 with the unvalidated [M1, M15].
|
||||||
|
"""
|
||||||
pair = _resolve_pair(args.pair)
|
pair = _resolve_pair(args.pair)
|
||||||
|
return PAIR_PRODUCERS[pair](_TF_BY_NAME[args.timeframe], None)
|
||||||
tf_map = {
|
|
||||||
"M5": Timeframe.M5, "M15": Timeframe.M15, "M30": Timeframe.M30,
|
|
||||||
"H1": Timeframe.H1, "H4": Timeframe.H4, "D1": Timeframe.D1,
|
|
||||||
}
|
|
||||||
primary_tf = tf_map[args.timeframe]
|
|
||||||
|
|
||||||
tf_confirmations = {
|
|
||||||
"M5": [Timeframe.M1, Timeframe.M15],
|
|
||||||
"M15": [Timeframe.M5, Timeframe.H1],
|
|
||||||
"M30": [Timeframe.M15, Timeframe.H1],
|
|
||||||
"H1": [Timeframe.M15, Timeframe.H4],
|
|
||||||
"H4": [Timeframe.H1, Timeframe.D1],
|
|
||||||
"D1": [Timeframe.H4, Timeframe.W1],
|
|
||||||
}
|
|
||||||
confirmation_tfs = tf_confirmations.get(
|
|
||||||
args.timeframe, [Timeframe.M15, Timeframe.H4]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Per-pair tuned configs from the pair_signals factories.
|
|
||||||
factory = PAIR_PRODUCERS.get(pair)
|
|
||||||
if factory is not None:
|
|
||||||
return factory(primary_tf, confirmation_tfs)
|
|
||||||
|
|
||||||
config = PlatformConfig()
|
|
||||||
config.primary_timeframe = primary_tf
|
|
||||||
config.confirmation_timeframes = confirmation_tfs
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def run_live(args, config: PlatformConfig):
|
def run_live(args, config: PlatformConfig):
|
||||||
|
|||||||
+3
-9
@@ -497,15 +497,9 @@ def _run_signal_sync(
|
|||||||
timeframe, [Timeframe.M15, Timeframe.H4]
|
timeframe, [Timeframe.M15, Timeframe.H4]
|
||||||
)
|
)
|
||||||
# Per-pair tuned configs (regime weights, gates, sessions) come from
|
# Per-pair tuned configs (regime weights, gates, sessions) come from
|
||||||
# the pair_signals factories; a plain PlatformConfig would silently
|
# the pair_signals factories; every scanned symbol is registered there
|
||||||
# run every pair with XAU defaults.
|
# (SCAN_PAIRS is validated against PAIR_INFO at import).
|
||||||
factory = PAIR_PRODUCERS.get(symbol)
|
config = PAIR_PRODUCERS[symbol](timeframe, confirmation_tfs)
|
||||||
if factory is not None:
|
|
||||||
config = factory(timeframe, confirmation_tfs)
|
|
||||||
else:
|
|
||||||
config = PlatformConfig()
|
|
||||||
config.primary_timeframe = timeframe
|
|
||||||
config.confirmation_timeframes = confirmation_tfs
|
|
||||||
aggregator = SignalAggregator(config)
|
aggregator = SignalAggregator(config)
|
||||||
aggregator.data_provider = _get_shared_dp()
|
aggregator.data_provider = _get_shared_dp()
|
||||||
return aggregator.analyze_pair(symbol=symbol, log_source="telegram")
|
return aggregator.analyze_pair(symbol=symbol, log_source="telegram")
|
||||||
|
|||||||
+8
-3
@@ -1594,14 +1594,19 @@ def _():
|
|||||||
assert Timeframe.D1 in config.confirmation_timeframes
|
assert Timeframe.D1 in config.confirmation_timeframes
|
||||||
|
|
||||||
|
|
||||||
@test("build_config all valid timeframes")
|
@test("build_config: every CLI timeframe uses the pair factory's confirmation map")
|
||||||
def _():
|
def _():
|
||||||
for tf_str in ["M5", "M15", "M30", "H1", "H4", "D1"]:
|
from pair_signals import xauusd as _xau
|
||||||
|
for tf_str, tf in (("M5", Timeframe.M5), ("M15", Timeframe.M15),
|
||||||
|
("H1", Timeframe.H1), ("H4", Timeframe.H4)):
|
||||||
class FakeArgs:
|
class FakeArgs:
|
||||||
pair = "XAUUSD"
|
pair = "XAUUSD"
|
||||||
timeframe = tf_str
|
timeframe = tf_str
|
||||||
config = build_config(FakeArgs())
|
config = build_config(FakeArgs())
|
||||||
assert config.primary_timeframe is not None
|
assert config.primary_timeframe == tf
|
||||||
|
# The CLI must not carry its own confirmation table: it once gave
|
||||||
|
# M5 an unvalidated [M1, M15] pairing that no other surface used.
|
||||||
|
assert config.confirmation_timeframes == _xau._CONFIRMATIONS[tf], tf_str
|
||||||
|
|
||||||
|
|
||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user