Add yfinance fallback for gold/silver + coarse Telegram progress
- yfinance>=0.2.40 added as a hard dependency
- _fetch_yfinance_fallback(): pulls OHLCV from Yahoo for symbols not on
the Twelve Data free tier (XAUUSD → GC=F, XAGUSD → SI=F)
- Hooked into both batch and single-symbol fetch paths; logs as [yf]
to distinguish from Twelve Data [live]
- Fail-loud: if both providers return nothing, the pair is skipped with
the existing "data unavailable" message
- _analyze_all_tfs accepts an async progress callback; both /signal and
/scan now show coarse milestones via message edits:
"📡 Fetching market data..." → "🧠 Analyzing pairs..." → result
- prefetch_batch is now run in a thread so it doesn't block the event loop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-4
@@ -276,7 +276,7 @@ class ForexTelegramBot:
|
||||
return SignalAggregator(self._build_config(tf, pairs), data_provider=provider).analyze_all_pairs()
|
||||
|
||||
async def _analyze_all_tfs(
|
||||
self, pairs: List[str]
|
||||
self, pairs: List[str], progress=None,
|
||||
) -> Dict[str, List[SignalReport]]:
|
||||
"""Single combined prefetch, then run all SCAN_TIMEFRAMES concurrently off the warm cache."""
|
||||
# Collect all unique timeframes needed across every TF scan
|
||||
@@ -286,9 +286,16 @@ class ForexTelegramBot:
|
||||
for tf in [_TF_MAP[tf_name]] + _TF_CONFIRMATIONS.get(tf_name, [])
|
||||
})
|
||||
provider = ForexDataProvider(PlatformConfig())
|
||||
provider.prefetch_batch(list(pairs), all_tfs)
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
if progress:
|
||||
await progress(f"📡 Fetching market data ({len(all_tfs)} timeframes × {len(pairs)} pairs)…")
|
||||
# Run blocking prefetch off the event loop so we don't freeze the bot
|
||||
await loop.run_in_executor(None, provider.prefetch_batch, list(pairs), all_tfs)
|
||||
|
||||
if progress:
|
||||
await progress("🧠 Analyzing pairs across timeframes…")
|
||||
|
||||
tasks = {
|
||||
tf: loop.run_in_executor(self._executor, self._run_scan, tf, list(pairs), provider)
|
||||
for tf in SCAN_TIMEFRAMES
|
||||
@@ -368,8 +375,15 @@ class ForexTelegramBot:
|
||||
msg = await update.message.reply_text(
|
||||
f"🔄 Analyzing {_h(display)} across {_h(tfs_str)}…"
|
||||
)
|
||||
|
||||
async def progress(text: str):
|
||||
try:
|
||||
await msg.edit_text(text)
|
||||
except Exception:
|
||||
pass # ignore edit-rate errors / no-change errors
|
||||
|
||||
try:
|
||||
results_by_tf = await self._analyze_all_tfs([raw])
|
||||
results_by_tf = await self._analyze_all_tfs([raw], progress=progress)
|
||||
now_str = _now_est()
|
||||
|
||||
# Check we got at least one result
|
||||
@@ -396,8 +410,15 @@ class ForexTelegramBot:
|
||||
msg = await update.message.reply_text(
|
||||
f"🔄 Scanning all pairs across {_h(tfs_str)}…"
|
||||
)
|
||||
|
||||
async def progress(text: str):
|
||||
try:
|
||||
await msg.edit_text(text)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
results_by_tf = await self._analyze_all_tfs(list(MAJOR_PAIRS))
|
||||
results_by_tf = await self._analyze_all_tfs(list(MAJOR_PAIRS), progress=progress)
|
||||
now_str = _now_est()
|
||||
|
||||
# Best grade per pair across all TFs
|
||||
|
||||
Reference in New Issue
Block a user