""" TradeSense AI — Master Quantitative Research Engine Orchestrator Executes the end-to-end reproducible research workflow for any ticker and cutoff date. """ from datetime import date from typing import Optional, Dict, Any, Tuple import pandas as pd import numpy as np from backend.app.schemas.contracts import ( StructuredEvidence, PredictionJournalEntry, DatasetMetadata, FeatureMetadata, SignalType ) from quant.data.fetcher import CanonicalDataEngine from quant.features.feature_store import FeatureStoreEngine from quant.backtesting.engine import WalkForwardEngine from quant.risk.risk_engine import RiskEngine from quant.evaluation.evidence_builder import EvidenceBuilder from quant.journal.journal_engine import PredictionJournalEngine from quant.copilot.explainer import GroundedCopilotEngine class TradeSenseMasterEngine: """ Main entry point for executing reproducible quantitative research runs. """ def __init__( self, polygon_api_key: Optional[str] = None, alpha_vantage_api_key: Optional[str] = None, fmp_api_key: Optional[str] = None, twelve_data_api_key: Optional[str] = None ): self.data_engine = CanonicalDataEngine( polygon_api_key=polygon_api_key, alpha_vantage_api_key=alpha_vantage_api_key, fmp_api_key=fmp_api_key, twelve_data_api_key=twelve_data_api_key ) self.journal_engine = PredictionJournalEngine() self.last_df_clean: Dict[str, pd.DataFrame] = {} def get_recent_history(self, ticker: str, n_bars: int = 45) -> Dict[str, Any]: """Returns recent clean historical OHLCV series for continuous frontend charts.""" ticker = ticker.upper().strip() df = self.last_df_clean.get(ticker) if df is None or df.empty: return {"sessions": [], "closes": [], "opens": [], "highs": [], "lows": [], "volumes": []} tail_df = df.tail(n_bars) return { "sessions": [d.strftime('%Y-%m-%d') for d in tail_df["session"]], "closes": [round(float(c), 2) for c in tail_df["Close"]], "opens": [round(float(o), 2) for o in tail_df["Open"]], "highs": [round(float(h), 2) for h in tail_df["High"]], "lows": [round(float(l), 2) for l in tail_df["Low"]], "volumes": [int(v) for v in tail_df["Volume"]] } def run_research_pipeline( self, ticker: str, cutoff_session: Optional[date] = None, horizon_sessions: int = 21 ) -> Tuple[StructuredEvidence, PredictionJournalEntry, str, DatasetMetadata]: """ Executes the complete end-to-end quantitative pipeline strictly at cutoff session T: 1. Fetch & clean canonical dataset 2. Build supervised feature windows (zero lookahead) 3. Run walk-forward simulation vs. Naive baseline with slippage 4. Generate probabilistic quantile forecast distribution 5. Compute ATR risk and trade target geometry 6. Emit schema-compliant StructuredEvidence JSON object 7. Synthesize grounded AI research note 8. Log immutable PredictionJournal entry """ ticker = ticker.upper().strip() # Step 1: Ingest and clean canonical historical data strictly <= cutoff T df_clean, dataset_meta = self.data_engine.build_canonical_dataset( ticker=ticker, years=10, cutoff_session=cutoff_session ) self.last_df_clean[ticker] = df_clean # Step 2: Build supervised feature windows X, Y, sessions, feature_cols, target_scaler = FeatureStoreEngine.build_supervised_windows( df_clean=df_clean, horizon_sessions=horizon_sessions, lookback_sessions=120 ) # Step 3: Run Walk-Forward evaluation vs Naive baseline with frictions feat_df = FeatureStoreEngine.calculate_technical_features(df_clean).dropna().reset_index(drop=True) close_prices = feat_df["Close"].values[120 - 1 : len(feat_df) - horizon_sessions] wf_engine = WalkForwardEngine(n_splits=4, test_window_size=63, slippage_bps=5.0) candidate_metrics, naive_metrics, statistical_diagnostics = wf_engine.evaluate_model( X=X, Y=Y, close_prices=close_prices, sessions=sessions, horizon_sessions=horizon_sessions ) # Step 4: Extract live inference features at cutoff T X_live, live_cutoff, current_price, _, feature_meta = FeatureStoreEngine.get_latest_inference_features( df_clean=df_clean, lookback_sessions=120 ) # Train candidate model with split conformal calibration on historical data <= T from quant.models.conformal_predictor import SplitConformalReturnPredictor cal_size = max(10, int(len(X) * 0.20)) X_tr, X_cal = X[:-cal_size], X[-cal_size:] p_tr, p_cal = close_prices[:-cal_size], close_prices[-cal_size:] y_tr_rets = (Y[:-cal_size] - p_tr) / np.where(p_tr > 0, p_tr, 1.0) y_cal_rets = (Y[-cal_size:] - p_cal) / np.where(p_cal > 0, p_cal, 1.0) active_model = SplitConformalReturnPredictor(n_estimators=120, max_depth=4, random_state=42) active_model.fit_and_calibrate(X_tr, y_tr_rets, X_cal, y_cal_rets) conf_res = active_model.predict_distribution(X_live, current_prices=np.array([current_price])) expected_price = float(conf_res.point_forecast_price[0]) interval_80 = (float(conf_res.interval_80_price[0][0]), float(conf_res.interval_80_price[1][0])) interval_95 = (float(conf_res.interval_95_price[0][0]), float(conf_res.interval_95_price[1][0])) # Step 5: Compute Risk Profile & Volatility Sizing risk_profile = RiskEngine.compute_risk_profile( df_clean=df_clean, expected_price=expected_price ) # Step 6: Determine Active Regime & Build Structured Evidence market_regime = EvidenceBuilder.determine_regime(df_clean) evidence = EvidenceBuilder.evaluate_signal_and_evidence( ticker=ticker, cutoff_session=live_cutoff, current_price=current_price, expected_price=expected_price, interval_80=interval_80, interval_95=interval_95, backtest=candidate_metrics, risk=risk_profile, market_regime=market_regime, dataset_version=dataset_meta.dataset_version, feature_version=feature_meta.feature_version, dataset_lineage_hash=dataset_meta.sha256_hash, horizon_sessions=horizon_sessions, data_quality_report=dataset_meta.quality_report, statistical_diagnostics=statistical_diagnostics, ) # Step 7: Synthesize Grounded AI Research Note research_note = GroundedCopilotEngine.generate_research_note(evidence) # Step 8: Log to Prediction Journal journal_entry = self.journal_engine.log_issued_forecast(evidence, current_price=current_price) return evidence, journal_entry, research_note, dataset_meta