""" Modèles SQLAlchemy - Trading AI Secure. Tables : - Trade : trades exécutés (ouverts + fermés) - OHLCVData : données de marché OHLCV (hypertable TimescaleDB) - BacktestResult : résultats de backtesting - MLModelMeta : métadonnées des modèles ML (date entraînement, métriques) - OptimizationRun : historique des runs Optuna TimescaleDB hypertable pour OHLCVData : SELECT create_hypertable('ohlcv', 'timestamp', if_not_exists => TRUE); (Exécuter une seule fois après création de la table) """ from datetime import datetime from typing import Optional from sqlalchemy import ( Boolean, Column, DateTime, Float, Index, Integer, JSON, String, Text, UniqueConstraint, ) from sqlalchemy.orm import DeclarativeBase class Base(DeclarativeBase): pass # ============================================================================= # TRADING # ============================================================================= class Trade(Base): """Trade exécuté (paper ou live).""" __tablename__ = "trades" id = Column(Integer, primary_key=True, autoincrement=True) symbol = Column(String(20), nullable=False, index=True) direction = Column(String(5), nullable=False) # LONG | SHORT quantity = Column(Float, nullable=False) entry_price = Column(Float, nullable=False) exit_price = Column(Float, nullable=True) stop_loss = Column(Float, nullable=False) take_profit = Column(Float, nullable=False) strategy = Column(String(50), nullable=False, index=True) mode = Column(String(10), nullable=False, default="paper") # paper | live entry_time = Column(DateTime, nullable=False, default=datetime.utcnow) exit_time = Column(DateTime, nullable=True) pnl = Column(Float, nullable=True) pnl_pct = Column(Float, nullable=True) risk_amount = Column(Float, nullable=True) status = Column(String(10), nullable=False, default="open") # open | closed close_reason = Column(String(30), nullable=True) # stop_loss | take_profit | manual | paper_end # Référence broker (IG Markets deal ID) deal_id = Column(String(50), nullable=True, unique=True) # Contexte ML ml_confidence = Column(Float, nullable=True) market_regime = Column(String(20), nullable=True) # bull | bear | sideways | volatile created_at = Column(DateTime, default=datetime.utcnow) __table_args__ = ( Index("ix_trades_entry_time", "entry_time"), ) def __repr__(self) -> str: return f"" # ============================================================================= # DONNÉES DE MARCHÉ # ============================================================================= class OHLCVData(Base): """ Données OHLCV (Open/High/Low/Close/Volume). Optimisé pour TimescaleDB : créer l'hypertable manuellement après migration : SELECT create_hypertable('ohlcv', 'timestamp', if_not_exists => TRUE); """ __tablename__ = "ohlcv" id = Column(Integer, primary_key=True, autoincrement=True) symbol = Column(String(20), nullable=False) timeframe = Column(String(5), nullable=False) # 1m, 5m, 15m, 1h, 1d timestamp = Column(DateTime, nullable=False) open = Column(Float, nullable=False) high = Column(Float, nullable=False) low = Column(Float, nullable=False) close = Column(Float, nullable=False) volume = Column(Float, nullable=True) source = Column(String(30), nullable=True) # yahoo_finance | alpha_vantage | ig_markets __table_args__ = ( UniqueConstraint("symbol", "timeframe", "timestamp", name="uq_ohlcv"), Index("ix_ohlcv_symbol_tf_ts", "symbol", "timeframe", "timestamp"), ) def __repr__(self) -> str: return f"" # ============================================================================= # BACKTESTING # ============================================================================= class BacktestResult(Base): """Résultat d'un run de backtesting.""" __tablename__ = "backtest_results" id = Column(Integer, primary_key=True, autoincrement=True) strategy = Column(String(50), nullable=False) symbol = Column(String(20), nullable=False) period = Column(String(10), nullable=False) # 6m | 1y | 2y initial_capital = Column(Float, nullable=False) final_capital = Column(Float, nullable=False) # Métriques de performance total_return = Column(Float, nullable=False) sharpe_ratio = Column(Float, nullable=False) max_drawdown = Column(Float, nullable=False) win_rate = Column(Float, nullable=False) profit_factor = Column(Float, nullable=False) total_trades = Column(Integer, nullable=False) calmar_ratio = Column(Float, nullable=True) sortino_ratio = Column(Float, nullable=True) # Validation is_valid = Column(Boolean, default=False) # Sharpe > 1.5, DD < 10%, etc. # Paramètres utilisés params = Column(JSON, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) def __repr__(self) -> str: return ( f"" ) # ============================================================================= # MACHINE LEARNING # ============================================================================= class MLModelMeta(Base): """Métadonnées d'un modèle ML entraîné.""" __tablename__ = "ml_models" id = Column(Integer, primary_key=True, autoincrement=True) model_name = Column(String(50), nullable=False) # xgboost | lightgbm | catboost | hmm_regime strategy = Column(String(50), nullable=True) version = Column(Integer, nullable=False, default=1) # Métriques d'entraînement train_sharpe = Column(Float, nullable=True) val_sharpe = Column(Float, nullable=True) train_accuracy = Column(Float, nullable=True) val_accuracy = Column(Float, nullable=True) # Paramètres hyperparams = Column(JSON, nullable=True) feature_names = Column(JSON, nullable=True) # liste des features utilisées # Chemin fichier modèle sérialisé file_path = Column(String(255), nullable=True) trained_at = Column(DateTime, default=datetime.utcnow) is_active = Column(Boolean, default=False) __table_args__ = ( Index("ix_ml_models_name_version", "model_name", "version"), ) def __repr__(self) -> str: return f"" class OptimizationRun(Base): """Historique des runs d'optimisation Optuna.""" __tablename__ = "optimization_runs" id = Column(Integer, primary_key=True, autoincrement=True) strategy = Column(String(50), nullable=False) n_trials = Column(Integer, nullable=False) best_sharpe = Column(Float, nullable=True) best_params = Column(JSON, nullable=True) drift_detected = Column(Boolean, default=False) duration_secs = Column(Float, nullable=True) started_at = Column(DateTime, default=datetime.utcnow) completed_at = Column(DateTime, nullable=True) def __repr__(self) -> str: return f""