tvsignals-to-tg/app/watchlist.py
Artemii Peretiachenko cdbda8fea3 Replace TradingView polling with a local LTF/FVG scanner that posts Telegram cards and Heryon webhooks.
Per-strategy Heryon accounts, reversal captions with previous-trade path on charts, and Telegram replies chained by ticker.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 20:37:16 +02:00

54 lines
1.6 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Any
import yaml
@dataclass(frozen=True)
class StrategyWatch:
id: str
enabled: bool
timeframe: str
telegram_thread_id: int | None
@dataclass(frozen=True)
class Watchlist:
symbols: list[str]
strategies: dict[str, StrategyWatch]
def load_watchlist(path: str | Path) -> Watchlist:
raw_path = Path(path)
if not raw_path.is_file():
raise FileNotFoundError(f"Watchlist not found: {raw_path}")
data: Any = yaml.safe_load(raw_path.read_text(encoding="utf-8")) or {}
symbols_raw = data.get("symbols") or []
symbols = [str(s).strip().upper() for s in symbols_raw if str(s).strip()]
if not symbols:
raise ValueError("Watchlist has no symbols")
strategies: dict[str, StrategyWatch] = {}
for key, cfg in (data.get("strategies") or {}).items():
if not isinstance(cfg, dict):
continue
strategies[str(key)] = StrategyWatch(
id=str(key),
enabled=bool(cfg.get("enabled", True)),
timeframe=str(cfg.get("timeframe", "")).strip(),
telegram_thread_id=_optional_int(cfg.get("telegram_thread_id")),
)
if "ltf" not in strategies:
strategies["ltf"] = StrategyWatch("ltf", True, "15m", None)
if "fvg" not in strategies:
strategies["fvg"] = StrategyWatch("fvg", True, "6h", None)
return Watchlist(symbols=symbols, strategies=strategies)
def _optional_int(value: object) -> int | None:
if value is None or value == "":
return None
return int(value)