Switch data source to Finnhub API; remove synthetic data fallback
Adds FinnhubDataProvider as a drop-in replacement for ForexDataProvider. Rate limit or API errors raise DataUnavailableError immediately — the Telegram bot shows a clean per-TF error card rather than fake prices. - data_provider_finnhub.py: Finnhub client, OANDA symbol mapping, 4h resampled from 60-min candles, shared module-level rate limiter (60 calls/min free tier), full OHLCV validation copied from original - signal_aggregator.py: import FinnhubDataProvider alias ForexDataProvider - config.py: add finnhub_api_key field (FINNHUB_API_KEY env var overrides) - telegram_bot.py: remove synthetic-data warning and _SUPPORTED_PAIRS; unsupported pairs now show the engine's "data unavailable" card - requirements.txt: add finnhub-python>=1.0 Set FINNHUB_API_KEY env var before running. main branch is untouched. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+6
-13
@@ -30,7 +30,7 @@ from telegram.constants import ParseMode
|
||||
from telegram.ext import Application, CommandHandler, ContextTypes
|
||||
|
||||
from config import (
|
||||
MAJOR_PAIRS, CROSS_PAIRS, SYMBOL_DISPLAY,
|
||||
MAJOR_PAIRS, SYMBOL_DISPLAY,
|
||||
PlatformConfig, SignalStrength, Timeframe,
|
||||
)
|
||||
from signal_aggregator import SignalAggregator, SignalReport
|
||||
@@ -54,8 +54,7 @@ _TF_CONFIRMATIONS = {
|
||||
"D1": [Timeframe.H4, Timeframe.W1],
|
||||
}
|
||||
|
||||
_SUPPORTED_PAIRS = set(MAJOR_PAIRS + CROSS_PAIRS)
|
||||
_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}
|
||||
_EST = ZoneInfo("America/New_York")
|
||||
|
||||
# ── Formatting helpers ──────────────────────────────────────────────
|
||||
@@ -114,7 +113,6 @@ def _build_combined_signal(
|
||||
results_by_tf: Dict[str, List[SignalReport]],
|
||||
display: str,
|
||||
now_str: str,
|
||||
synthetic: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Single message showing all timeframes stacked.
|
||||
@@ -125,11 +123,7 @@ def _build_combined_signal(
|
||||
lines.append(f"🌐 <b>{_h(display)} — Signal Analysis</b>")
|
||||
lines.append(f"🕐 {now_str}")
|
||||
|
||||
if synthetic:
|
||||
lines.append(
|
||||
"⚠️ <b>Note:</b> Not on Twelve Data free tier — "
|
||||
"prices are <b>simulated</b> data."
|
||||
)
|
||||
# Finnhub branch: no synthetic data — real prices or error shown per-TF.
|
||||
|
||||
for tf in SCAN_TIMEFRAMES:
|
||||
reports = results_by_tf.get(tf, [])
|
||||
@@ -360,9 +354,8 @@ class ForexTelegramBot:
|
||||
raw = args[0].upper().replace("/", "").replace("_", "")
|
||||
if not raw.endswith("=X"):
|
||||
raw += "=X"
|
||||
display = SYMBOL_DISPLAY.get(raw, raw.replace("=X", ""))
|
||||
tfs_str = " · ".join(SCAN_TIMEFRAMES)
|
||||
synthetic = raw not in _SUPPORTED_PAIRS
|
||||
display = SYMBOL_DISPLAY.get(raw, raw.replace("=X", ""))
|
||||
tfs_str = " · ".join(SCAN_TIMEFRAMES)
|
||||
|
||||
msg = await update.message.reply_text(
|
||||
f"🔄 Analyzing {_h(display)} across {_h(tfs_str)}…"
|
||||
@@ -376,7 +369,7 @@ class ForexTelegramBot:
|
||||
await msg.edit_text(f"❌ No data returned for {_h(display)}")
|
||||
return
|
||||
|
||||
text = _build_combined_signal(results_by_tf, display, now_str, synthetic)
|
||||
text = _build_combined_signal(results_by_tf, display, now_str)
|
||||
await msg.delete()
|
||||
await self._send_html(update, text)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user