from __future__ import annotations from typing import Any from backend.bootstrap.config import display_compact_date as _display_date from backend.data.numbers import finite_number as _number from backend.features.screener.backtest import BacktestRunner from backend.features.screener.catalog import ( ADVANCED_CURATED_STRATEGIES, ALLOWED_OPERATORS, BUILTIN_STRATEGIES, CURATED_STRATEGIES, FACTOR_FIELDS, FACTOR_GROUPS, REGIMES, STRATEGY_ENVIRONMENT_NOTES, ) from backend.features.screener.data_sync import ( FactorDataService, _earnings_event_rows, _popularity_factor_rows, _quarter_periods, ) from backend.features.screener.factors import FactorBuilder from backend.features.screener.formula import FormulaEvaluator, compile_local_strategy from backend.features.screener.indicators import ( _available_percentile_map, _broken_reversal_metrics, _ema, _ending_streak, _is_limit_bar, _limit_threshold, _macd_last, _macd_series, _matches, _max_streak, _optional_number, _pearson, _percentile_map, _regime_reason, _risk_flags, _rounded_optional, _rsi, _touched_limit_bar, _weekly_series, ) from backend.features.screener.regime import RegimeDetector from backend.features.screener.selection import SelectionRunner from database import ReviewDatabase class ScreenerEngine: """Stable facade over the independently owned screener services.""" def __init__(self, database: ReviewDatabase) -> None: self.database = database self.factor_builder = FactorBuilder(database) self.formula_evaluator = FormulaEvaluator() self.regime_detector = RegimeDetector(database) self.backtest_runner = BacktestRunner( database, self.factor_builder, self.formula_evaluator ) self.selection_runner = SelectionRunner( database, self.factor_builder, self.formula_evaluator, self.backtest_runner, ) def ensure_builtin_strategies(self) -> None: existing = { item["name"]: item for item in self.database.list_screener_strategies() if item["builtin"] } for strategy in BUILTIN_STRATEGIES: current = existing.get(strategy["name"]) self.database.save_screener_strategy( None, **strategy, builtin=True, strategy_id=int(current["id"]) if current else None, ) def detect_regime(self, trade_date: str) -> dict[str, Any]: return self.regime_detector.detect_regime(trade_date) def factor_health(self, trade_date: str) -> dict[str, Any]: return self.database.factor_health_summary(trade_date) def validate_formula(self, formula: dict[str, Any]) -> dict[str, Any]: return self.formula_evaluator.validate_formula(formula) def screen( self, user_id: int, trade_date: str, formula: dict[str, Any], regime: str, strategy_name: str, run_backtest: bool = True, realtime_snapshot: dict[str, Any] | None = None, mode: str = "smart", prepared_factors: list[dict[str, Any]] | None = None, prepared_date: str = "", ) -> dict[str, Any]: return self.selection_runner.screen( user_id, trade_date, formula, regime, strategy_name, run_backtest, realtime_snapshot, mode, prepared_factors, prepared_date, ) def build_factors( self, trade_date: str, realtime_snapshot: dict[str, Any] | None = None, history_days: int = 80, ) -> tuple[list[dict[str, Any]], str]: return self.factor_builder.build_factors( trade_date, realtime_snapshot, history_days ) def apply_formula( self, rows: list[dict[str, Any]], formula: dict[str, Any], regime: str ) -> list[dict[str, Any]]: return self.formula_evaluator.apply_formula(rows, formula, regime) def backtest(self, trade_date: str, formula: dict[str, Any]) -> dict[str, Any]: return self.backtest_runner.backtest(trade_date, formula)