mirror of
https://github.com/artemium428/tvsignals-to-tg.git
synced 2026-09-15 17:16:21 +00:00
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>
54 lines
1.6 KiB
Python
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)
|