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>
204 lines
7.6 KiB
Python
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}>"
|