Scan M15/H1/D1/W1 only; grade symbols + legend on all messages

- SCAN_TIMEFRAMES: drop H4, add W1 (4 unique TFs → fewer API calls)
- Confirmations updated to use only the 4 scan TFs (no H4 dependency)
- Add _grade_symbol(): 🔥A+ ✅A 👍B ⚠️C 📉D ❌F
- _GRADE_LEGEND shown on every /signal and /scan message
- 🧠 icon on Signal Analysis and Scan Complete headers
- /help grade guide updated with full symbol legend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Harwinder
2026-05-04 21:29:48 -04:00
parent 775655e0ac
commit 683d070a41
+25 -12
View File
@@ -40,22 +40,24 @@ logger = logging.getLogger(__name__)
# ── Config ────────────────────────────────────────────────────────── # ── Config ──────────────────────────────────────────────────────────
SCAN_TIMEFRAMES = ["M15", "H1", "H4", "D1"] SCAN_TIMEFRAMES = ["M15", "H1", "D1", "W1"]
_TF_MAP = { _TF_MAP = {
"M5": Timeframe.M5, "M15": Timeframe.M15, "M5": Timeframe.M5, "M15": Timeframe.M15,
"M30": Timeframe.M30, "H1": Timeframe.H1, "M30": Timeframe.M30, "H1": Timeframe.H1,
"H4": Timeframe.H4, "D1": Timeframe.D1, "H4": Timeframe.H4, "D1": Timeframe.D1,
"W1": Timeframe.W1,
} }
_TF_CONFIRMATIONS = { _TF_CONFIRMATIONS = {
"M15": [Timeframe.M5, Timeframe.H1], "M15": [Timeframe.H1, Timeframe.D1],
"H1": [Timeframe.M15, Timeframe.H4], "H1": [Timeframe.M15, Timeframe.D1],
"H4": [Timeframe.H1, Timeframe.D1], "D1": [Timeframe.H1, Timeframe.W1],
"D1": [Timeframe.H4, Timeframe.W1], "W1": [Timeframe.D1],
} }
_GRADE_RANK = {"A+": 0, "A": 1, "B": 2, "C": 3, "D": 4, "F": 5} _GRADE_RANK = {"A+": 0, "A": 1, "B": 2, "C": 3, "D": 4, "F": 5}
_GRADE_LEGEND = "Grade scale: 🔥 A+ · ✅ A · 👍 B · ⚠️ C · 📉 D · ❌ F"
_EST = ZoneInfo("America/New_York") _EST = ZoneInfo("America/New_York")
# ── Formatting helpers ────────────────────────────────────────────── # ── Formatting helpers ──────────────────────────────────────────────
@@ -75,6 +77,10 @@ def _signal_action(signal: SignalStrength) -> str:
}.get(signal, "NEUTRAL") }.get(signal, "NEUTRAL")
def _grade_symbol(quality: str) -> str:
return {"A+": "🔥", "A": "✅", "B": "👍", "C": "⚠️", "D": "📉"}.get(quality, "❌")
def _indicator_condition(ind) -> str: def _indicator_condition(ind) -> str:
desc = ind.description desc = ind.description
if " — " in desc: if " — " in desc:
@@ -114,8 +120,9 @@ def _build_combined_signal(
MTF detail block (folded to one line), news unless non-neutral. MTF detail block (folded to one line), news unless non-neutral.
""" """
lines: List[str] = [] lines: List[str] = []
lines.append(f"🌐 <b>{_h(display)} — Signal Analysis</b>") lines.append(f"🌐 🧠 <b>{_h(display)} — Signal Analysis</b>")
lines.append(f"🕐 {now_str}") lines.append(f"🕐 {now_str}")
lines.append(f"<i>{_GRADE_LEGEND}</i>")
for tf in SCAN_TIMEFRAMES: for tf in SCAN_TIMEFRAMES:
reports = results_by_tf.get(tf, []) reports = results_by_tf.get(tf, [])
@@ -137,10 +144,11 @@ def _build_combined_signal(
continue continue
action = _signal_action(r.final_signal) action = _signal_action(r.final_signal)
sym = _grade_symbol(r.signal_quality)
lines.append("") lines.append("")
lines.append("────────────────────────") lines.append("────────────────────────")
lines.append(f"<b>{tf} · {action} · Grade {_h(r.signal_quality)}</b>") lines.append(f"{sym} <b>{tf} · {action} · Grade {_h(r.signal_quality)}</b>")
lines.append("") lines.append("")
# ── Trade setup (always kept intact) ───────────────────────── # ── Trade setup (always kept intact) ─────────────────────────
@@ -207,15 +215,17 @@ def _build_scan_message(
Full entry/TP/SL preserved; no indicator list (use /signal to drill in). Full entry/TP/SL preserved; no indicator list (use /signal to drill in).
""" """
lines: List[str] = [] lines: List[str] = []
lines.append(f"📊 <b>Scan Complete</b> · {now_str}") lines.append(f"📊 🧠 <b>Scan Complete</b> · {now_str}")
lines.append(f"Timeframes: <code>{_h(tfs_str)}</code> | Best signal per pair") lines.append(f"Timeframes: <code>{_h(tfs_str)}</code> | Best signal per pair")
lines.append(f"<i>{_GRADE_LEGEND}</i>")
lines.append("") lines.append("")
for tf, r in actionable: for tf, r in actionable:
action = _signal_action(r.final_signal) action = _signal_action(r.final_signal)
sym = _grade_symbol(r.signal_quality)
lines.append( lines.append(
f"<b>{_h(r.display_name)} · {action}</b>" f"{sym} <b>{_h(r.display_name)} · {action}</b>"
f" · {_h(r.signal_quality)} · {tf}" f" · {_h(r.signal_quality)} · {tf}"
) )
@@ -325,9 +335,12 @@ class ForexTelegramBot:
" /help — This message\n\n" " /help — This message\n\n"
f"<b>Timeframes analyzed:</b> <code>{tfs}</code>\n\n" f"<b>Timeframes analyzed:</b> <code>{tfs}</code>\n\n"
"<b>Signal Grades:</b>\n" "<b>Signal Grades:</b>\n"
" A / A+ — High confidence, consider entry\n" " 🔥 A+ — Strongest signal, MTF aligned, high confidence\n"
" B — Decent setup, wait for confirmation\n" " ✅ A — Strong signal, MTF aligned\n"
" C / D — Low confidence, watch only\n\n" " 👍 B — Decent setup, wait for confirmation\n"
" ⚠️ C — Weak signal, watch only\n"
" 📉 D — Very weak, no action\n"
" ❌ F — No clear signal\n\n"
"<i>⚠️ Educational use only. Not financial advice.</i>" "<i>⚠️ Educational use only. Not financial advice.</i>"
) )
await update.message.reply_text(text, parse_mode=ParseMode.HTML) await update.message.reply_text(text, parse_mode=ParseMode.HTML)