Telegram /news command: net XAU/USD news sentiment score on demand
Owner-locked /news replies with the existing get_pair_sentiment() net score (XAU mean - USD mean, -1..+1) and its label; a caveat line marks the no-headlines case so a feed outage can't read as neutral. Fetch runs in the shared executor with a placeholder-then-edit reply. Display-only: news still never gates signals. Registered in handler block, command menu, help text, docstring, and user guide; 3 new builder tests (299). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017975c1TufZ3xRYAq8iWTCS
This commit is contained in:
@@ -19,6 +19,8 @@ WebSocket: Streams live ticks from the streaming provider — config.json
|
||||
EMA200) cached from the last boundary scan, triggers an
|
||||
immediate M15 scan and sends a Level Break Alert.
|
||||
5-minute cooldown prevents spam.
|
||||
News: /news replies with the net XAU/USD news sentiment score
|
||||
(display-only — news never gates signals).
|
||||
|
||||
Owner lock: All commands and buttons only respond to TELEGRAM_CHAT_ID
|
||||
(the owner). Everyone else gets a polite refusal — the bot
|
||||
@@ -69,6 +71,7 @@ from app_config import (
|
||||
telegram_channel_id,
|
||||
)
|
||||
from providers import BaseDataProvider, make_data_provider
|
||||
from news_engine import FundamentalContext, NewsEngine
|
||||
from pair_signals import PAIR_PRODUCERS
|
||||
from signal_aggregator import SignalAggregator, SignalReport
|
||||
|
||||
@@ -951,6 +954,7 @@ _HELP_TEXT = (
|
||||
"/scan — Quick scan menu\n"
|
||||
"/autoscan — Toggle auto-scan on/off\n"
|
||||
"/status — Bot status, WS connection, live prices\n"
|
||||
"/news — Net news sentiment score for XAU/USD\n"
|
||||
"/cache — Show data cache age per pair/TF\n"
|
||||
"/clearcache — Wipe data cache, force fresh fetch\n"
|
||||
"/help — This message\n\n"
|
||||
@@ -1030,6 +1034,36 @@ async def cmd_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None
|
||||
await update.message.reply_text(_status_text(), parse_mode=ParseMode.MARKDOWN)
|
||||
|
||||
|
||||
def _news_text(context: FundamentalContext, engine: NewsEngine) -> str:
|
||||
net, desc = engine.get_pair_sentiment(XAUUSD, context)
|
||||
lines = [
|
||||
"📰 *XAU/USD News Score*\n",
|
||||
f"*Net score:* `{net:+.2f}` — {desc}",
|
||||
]
|
||||
# A cold/failed feed yields 0.00 "Neutral" — flag it so a data outage
|
||||
# can't be mistaken for a genuinely neutral read.
|
||||
if not context.news_sentiment:
|
||||
lines.append("_no scored headlines yet — score defaults to 0_")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _news_report_sync() -> str:
|
||||
engine = NewsEngine(PlatformConfig().news)
|
||||
return _news_text(engine.get_fundamental_context(), engine)
|
||||
|
||||
|
||||
@_owner_only
|
||||
async def cmd_news(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
msg = await update.message.reply_text("⏳ Fetching news…")
|
||||
loop = asyncio.get_running_loop()
|
||||
try:
|
||||
text = await loop.run_in_executor(_executor, _news_report_sync)
|
||||
except Exception as exc:
|
||||
logger.exception("News fetch failed")
|
||||
text = f"❌ News fetch failed: {_escape_md(str(exc))}"
|
||||
await msg.edit_text(text, parse_mode=ParseMode.MARKDOWN)
|
||||
|
||||
|
||||
@_owner_only
|
||||
async def cmd_cache(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
dp = _get_shared_dp()
|
||||
@@ -1245,6 +1279,7 @@ async def _post_init(app: Application) -> None:
|
||||
("scan", "Quick scan buttons"),
|
||||
("autoscan", "Toggle auto-scan on/off"),
|
||||
("status", "Bot status, WS connection, live prices"),
|
||||
("news", "XAU/USD net news sentiment score"),
|
||||
("cache", "Show data cache age per pair/TF"),
|
||||
("clearcache", "Wipe data cache, force fresh fetch"),
|
||||
("help", "All commands"),
|
||||
@@ -1374,6 +1409,7 @@ def main() -> None:
|
||||
app.add_handler(CommandHandler("scan", cmd_scan))
|
||||
app.add_handler(CommandHandler("help", cmd_help))
|
||||
app.add_handler(CommandHandler("status", cmd_status))
|
||||
app.add_handler(CommandHandler("news", cmd_news))
|
||||
app.add_handler(CommandHandler("autoscan", cmd_autoscan))
|
||||
app.add_handler(CommandHandler("cache", cmd_cache))
|
||||
app.add_handler(CommandHandler("clearcache", cmd_clearcache))
|
||||
|
||||
Reference in New Issue
Block a user