Add chart generation: /chart command + /signal sends chart
- charts.py: candlestick (mplfinance, dark theme) with EMA20/50 overlay, Bollinger Bands, RSI subplot and 30/70 reference lines - /signal now sends chart image with analysis as caption - New /chart <PAIR> <TF> command for standalone chart view Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ from telegram.ext import (
|
||||
)
|
||||
|
||||
from config import SUPPORTED_PAIRS, SUPPORTED_TIMEFRAMES, TELEGRAM_TOKEN
|
||||
from charts import generate as generate_chart
|
||||
from indicators import bollinger, macd, rsi
|
||||
from signals import HTF_OF, analyze, emoji
|
||||
from twelvedata import fetch_ohlc
|
||||
@@ -31,7 +32,8 @@ WELCOME = (
|
||||
"/rsi <PAIR> <TF> — RSI value\n"
|
||||
"/macd <PAIR> <TF> — MACD line/signal/histogram\n"
|
||||
"/bb <PAIR> <TF> — Bollinger Bands\n"
|
||||
"/signal <PAIR> <TF> — multi-indicator weighted verdict with HTF trend filter\n"
|
||||
"/signal <PAIR> <TF> — multi-indicator weighted verdict with chart\n"
|
||||
"/chart <PAIR> <TF> — candlestick chart with EMA/BB/RSI\n"
|
||||
"/help — this message"
|
||||
) + DISCLAIMER
|
||||
|
||||
@@ -131,6 +133,18 @@ def _format_reading(r) -> str:
|
||||
return f"• {r.name}: {r.value} ({sign}{r.contribution})"
|
||||
|
||||
|
||||
async def chart_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
try:
|
||||
pair, tf = _parse_pair_tf(ctx.args)
|
||||
df = await fetch_ohlc(pair, tf)
|
||||
img = generate_chart(df, pair, tf)
|
||||
caption = f"*{pair}* {tf} — candlestick · EMA20/50 · BB(20,2) · RSI(14)" + DISCLAIMER
|
||||
await update.message.reply_photo(img, caption=caption, parse_mode="Markdown")
|
||||
except Exception as e:
|
||||
logger.exception("chart_cmd failed")
|
||||
await _reply(update, f"Error: {e}")
|
||||
|
||||
|
||||
async def signal_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
try:
|
||||
pair, tf = _parse_pair_tf(ctx.args)
|
||||
@@ -177,7 +191,13 @@ async def signal_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if report["notes"]:
|
||||
lines += ["", *[f"_{n}_" for n in report["notes"]]]
|
||||
|
||||
await _reply(update, "\n".join(lines))
|
||||
caption = "\n".join(lines) + DISCLAIMER
|
||||
img = generate_chart(
|
||||
df, pair, tf,
|
||||
verdict=report["verdict"],
|
||||
confidence=report["confidence"],
|
||||
)
|
||||
await update.message.reply_photo(img, caption=caption, parse_mode="Markdown")
|
||||
except Exception as e:
|
||||
logger.exception("signal_cmd failed")
|
||||
await _reply(update, f"Error: {e}")
|
||||
@@ -191,7 +211,8 @@ BOT_COMMANDS: list[BotCommand] = [
|
||||
BotCommand("rsi", "RSI(14) — /rsi EURUSD 1h"),
|
||||
BotCommand("macd", "MACD(12,26,9) — /macd EURUSD 1h"),
|
||||
BotCommand("bb", "Bollinger Bands(20,2) — /bb EURUSD 1h"),
|
||||
BotCommand("signal", "Multi-indicator verdict — /signal EURUSD 1h"),
|
||||
BotCommand("signal", "Multi-indicator verdict + chart — /signal EURUSD 1h"),
|
||||
BotCommand("chart", "Candlestick chart with EMA/BB/RSI — /chart EURUSD 1h"),
|
||||
]
|
||||
|
||||
|
||||
@@ -216,6 +237,7 @@ def main() -> None:
|
||||
app.add_handler(CommandHandler("macd", macd_cmd))
|
||||
app.add_handler(CommandHandler("bb", bb_cmd))
|
||||
app.add_handler(CommandHandler("signal", signal_cmd))
|
||||
app.add_handler(CommandHandler("chart", chart_cmd))
|
||||
logger.info("Bot starting...")
|
||||
app.run_polling()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user