tradesenseAi / quant /engine.py
Niranjan-Murugarasu024
feat: TradeSense AI Chairman Edition production release ready for Hugging Face Spaces
0125724
Raw History Blame Contribute Delete
7.17 kB
"""
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