Conviction campaign 2026-09-27 (FAILED, research knob kept) + plain-words alert wording
Owner question: can score and confidence be combined into one number that is easier to understand and for the system to decide on? Research (pre-registered, nothing ships to the signal path): - signals.grading_mode: "pair" (default, the shipped two-floor rule, byte- identical: all five reference MD5s reproduce on both scripts), "min" (min(|score|, 0.6*conf)), "product" (|score|*conf/100); thresholds fixed from the control's corner points, no tuning. Helpers _final_signal_and_grade / _transitioning_boost / conviction / CONVICTION_THRESHOLDS in signal_aggregator; mirrored in K2 (GRADING_MODE_CODES, PF_conv_* packed from the live module); twin refuses other values; M15 OOS MD5 equal on both scripts for both modes. - Verdict: FAILED on M5 and M15. K-MIN moves 43-54 trades for -1.2R / -2.9R (clauses a,b,e / a,b,d); K-PROD -29R on M15 (PF 1.29, W3 DD 28.9R), neutral on M5. H1/H4 product +11.9R / +17.0R on W3 from extra low-score trades (H1 PF 1.98 -> 1.75, DD doubles): hypothesis only. - Code review (high) before the runs: 6 findings fixed, 1 recorded (engine_config_fingerprint omits the switch under "pair", so live rows keep their fingerprint). Delivery (books untouched): alerts lead with the grade and print the two raw numbers in words (main.plain_words: "Direction: strong buy · Reliability: good · timeframes agree", cut-offs = the grade table's own) on Telegram, /status, the CLI and the dashboard card (plain_words in the snapshot); "MTF" label replaced by "Other timeframes"; guides updated. Suite 566 passed. Evidence: backtest/research/conviction_campaign_2026-09-27.md Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+21
-7
@@ -398,12 +398,22 @@ def _book_rate_line(report: SignalReport) -> Optional[str]:
|
||||
f"{rate['win_rate']:.0f}% winners, avg {rate['mean_r']:+.2f}R per trade")
|
||||
|
||||
|
||||
def _plain_words_line(report: SignalReport) -> str:
|
||||
"""Grade first, then the two raw numbers in words (owner request
|
||||
2026-09-27): "Direction: strong buy · Reliability: good · timeframes
|
||||
agree" replaces "Score +38.2 · Conf 62% · MTF ✅". The words come from
|
||||
main.plain_words so every surface says the same thing."""
|
||||
from main import direction_words, reliability_words # display helper, no side effects
|
||||
agree = "timeframes agree" if report.mtf_agreement else "timeframes disagree"
|
||||
return (f"📐 Direction: *{direction_words(report.final_score)}* · "
|
||||
f"Reliability: *{reliability_words(report.final_confidence)}* · {agree}")
|
||||
|
||||
|
||||
def format_signal_message(report: SignalReport) -> str:
|
||||
sig = report.final_signal
|
||||
emoji = _SIGNAL_EMOJI.get(sig, "⬜")
|
||||
qe = _QUALITY_EMOJI.get(report.signal_quality, "")
|
||||
label = _signal_label(sig)
|
||||
mtf_tag = "MTF ✅" if report.mtf_agreement else "MTF ❌"
|
||||
tf = report.primary_analysis.timeframe if report.primary_analysis else ""
|
||||
|
||||
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
||||
@@ -411,14 +421,14 @@ def format_signal_message(report: SignalReport) -> str:
|
||||
vol_str = _escape_md(str(report.volatility_regime))
|
||||
|
||||
lines: list[str] = [
|
||||
f"{emoji} *{label} · {report.display_name} · {tf}* {qe}{report.signal_quality}",
|
||||
f"{emoji} *Grade {report.signal_quality} · {label} · {report.display_name} · {tf}* {qe}",
|
||||
f"🕐 {now} · 💰 {_fmt_price(report.current_price)}",
|
||||
]
|
||||
live = _live_price_line(report.symbol)
|
||||
if live:
|
||||
lines.append(live)
|
||||
lines += [
|
||||
f"Score *{report.final_score:+.1f}* · Conf *{report.final_confidence:.0f}%* · {mtf_tag}",
|
||||
_plain_words_line(report),
|
||||
f"📊 Regime: {regime_str} · Vol: {vol_str}",
|
||||
]
|
||||
if report.action_recommendation:
|
||||
@@ -498,7 +508,7 @@ def format_signal_message(report: SignalReport) -> str:
|
||||
extra: list[str] = []
|
||||
mtf = _mtf_line(report)
|
||||
if mtf:
|
||||
extra.append(f"📈 MTF: {mtf}")
|
||||
extra.append(f"📈 Other timeframes: {mtf}")
|
||||
ind_summary = _indicator_summary(report)
|
||||
if ind_summary:
|
||||
extra.append(f"📋 {ind_summary}")
|
||||
@@ -1852,6 +1862,7 @@ async def _cb_scan_all(query, symbol: str = XAUUSD) -> None:
|
||||
"",
|
||||
]
|
||||
|
||||
from main import direction_words, reliability_words # display helper (2026-09-27)
|
||||
for r in reports:
|
||||
emoji = _SIGNAL_EMOJI.get(r.final_signal, "⬜")
|
||||
qe = _QUALITY_EMOJI.get(r.signal_quality, "")
|
||||
@@ -1859,10 +1870,13 @@ async def _cb_scan_all(query, symbol: str = XAUUSD) -> None:
|
||||
sig = _signal_label(r.final_signal)
|
||||
regime = r.market_regime.replace("_", " ").title()
|
||||
lines.append(
|
||||
f"{emoji} *{tf_label}* {sig} {qe}{r.signal_quality} "
|
||||
f"Score: {r.final_score:+.1f} {r.final_confidence:.0f}%"
|
||||
f"{emoji} *{tf_label}* Grade {r.signal_quality} {qe} · {sig}"
|
||||
)
|
||||
lines.append(f" _{_escape_md(regime)}_ | {'✅ MTF' if r.mtf_agreement else '❌ MTF'}")
|
||||
lines.append(
|
||||
f" {direction_words(r.final_score)}, {reliability_words(r.final_confidence)} reliability, "
|
||||
f"{'timeframes agree' if r.mtf_agreement else 'timeframes disagree'}"
|
||||
)
|
||||
lines.append(f" _{_escape_md(regime)}_")
|
||||
if r.trade_setup and r.final_signal != SignalStrength.NEUTRAL:
|
||||
ts = r.trade_setup
|
||||
d = "📈" if ts.direction == "BUY" else "📉"
|
||||
|
||||
Reference in New Issue
Block a user