67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""Ticker universe — single source of truth.
|
|
|
|
All scanners that need a list of tickers should call load_universe(config).
|
|
Do NOT hardcode "data/tickers.txt" in scanner files — import this module instead.
|
|
|
|
Priority order:
|
|
1. config["discovery"]["universe"] — explicit list (tests / overrides)
|
|
2. config["tickers_file"] — path from top-level config
|
|
3. Default: data/tickers.txt resolved relative to repo root
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from tradingagents.utils.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
# Resolved once at import time — works regardless of cwd
|
|
_REPO_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
DEFAULT_TICKERS_FILE = str(_REPO_ROOT / "data" / "tickers.txt")
|
|
|
|
|
|
def load_universe(config: Optional[Dict[str, Any]] = None) -> List[str]:
|
|
"""Return the full ticker universe as a list of uppercase strings.
|
|
|
|
Args:
|
|
config: Top-level app config dict. If None, falls back to default file.
|
|
|
|
Returns:
|
|
Deduplicated list of ticker symbols in the order they appear in the file.
|
|
"""
|
|
cfg = config or {}
|
|
|
|
# 1. Explicit list in config (useful for tests or targeted overrides)
|
|
explicit = cfg.get("discovery", {}).get("universe")
|
|
if explicit:
|
|
tickers = [t.strip().upper() for t in explicit if t.strip()]
|
|
logger.info(f"Universe: {len(tickers)} tickers from config override")
|
|
return tickers
|
|
|
|
# 2. Config-specified file path, falling back to repo-relative default
|
|
file_path = cfg.get("tickers_file", DEFAULT_TICKERS_FILE)
|
|
return _load_from_file(file_path)
|
|
|
|
|
|
def _load_from_file(path: str) -> List[str]:
|
|
"""Load tickers from a text file (one per line, # comments ignored)."""
|
|
try:
|
|
with open(path) as f:
|
|
tickers = [
|
|
line.strip().upper()
|
|
for line in f
|
|
if line.strip() and not line.strip().startswith("#")
|
|
]
|
|
# Deduplicate while preserving order
|
|
seen: set = set()
|
|
unique = [t for t in tickers if not (t in seen or seen.add(t))]
|
|
logger.info(f"Universe: loaded {len(unique)} tickers from {path}")
|
|
return unique
|
|
except FileNotFoundError:
|
|
logger.warning(f"Ticker file not found: {path} — universe will be empty")
|
|
return []
|
|
except Exception as e:
|
|
logger.warning(f"Failed to load ticker file {path}: {e}")
|
|
return []
|