Files
trader-ml/src/db/models.py
Tika da30ef19ed Initial commit — Trading AI Secure project complet
Architecture Docker (8 services), FastAPI, TimescaleDB, Redis, Streamlit.
Stratégies : scalping, intraday, swing. MLEngine + RegimeDetector (HMM).
BacktestEngine + WalkForwardAnalyzer + Optuna optimizer.
Routes API complètes dont /optimize async.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 17:38:09 +00:00

204 lines
7.6 KiB
Python

"""
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"<Trade {self.direction} {self.symbol} @ {self.entry_price} [{self.status}]>"
# =============================================================================
# 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"<OHLCV {self.symbol} {self.timeframe} {self.timestamp} C={self.close}>"
# =============================================================================
# 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"<BacktestResult {self.strategy}/{self.symbol} "
f"Sharpe={self.sharpe_ratio:.2f} DD={self.max_drawdown:.1%}>"
)
# =============================================================================
# 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"<MLModelMeta {self.model_name} v{self.version} active={self.is_active}>"
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"<OptimizationRun {self.strategy} trials={self.n_trials} sharpe={self.best_sharpe}>"