Bug: les markers BUY/SELL du chart utilisaient les timestamps des trades du backtest mais les candles étaient fetchées séparément (500 candles récentes), causant un désalignement visuel. - Backend: /backtest retourne désormais les candles exactes du DataFrame analysé - Frontend: Backtest.tsx utilise result.candles directement (suppression du fetchCandles séparé) - Ajout: sérialisation reason/OB/LL sur les trades, overlays OB/LL bornés dans le temps, trade reasons expandables, filtre HTF, badge tendance HTF Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
|
|
import pandas as pd
|
|
|
|
|
|
@dataclass
|
|
class OrderBlockZone:
|
|
"""Zone Order Block à surveiller."""
|
|
id: str
|
|
direction: str # "bullish" | "bearish"
|
|
top: float # Haut de la zone
|
|
bottom: float # Bas de la zone
|
|
origin_time: pd.Timestamp
|
|
mitigated: bool = False # True si le prix a traversé la zone
|
|
|
|
|
|
@dataclass
|
|
class LiquidityLevel:
|
|
"""Niveau de liquidité (Equal H/L)."""
|
|
id: str
|
|
direction: str # "high" | "low"
|
|
price: float
|
|
origin_time: pd.Timestamp
|
|
swept: bool = False # True si le prix a dépassé ce niveau
|
|
|
|
|
|
@dataclass
|
|
class TradeReason:
|
|
"""Explication structurée de pourquoi un signal a été généré."""
|
|
summary: str # "EQH sweep at 1.08542 → Bullish OB [1.082 - 1.0835]"
|
|
swept_level_price: Optional[float] = None
|
|
swept_level_direction: Optional[str] = None # "high" | "low"
|
|
ob_direction: Optional[str] = None # "bullish" | "bearish"
|
|
ob_top: Optional[float] = None
|
|
ob_bottom: Optional[float] = None
|
|
ob_origin_time: Optional[str] = None
|
|
filters_applied: list[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class TradeSignal:
|
|
"""Signal de trading généré par la stratégie."""
|
|
direction: str # "buy" | "sell"
|
|
entry_price: float
|
|
stop_loss: float
|
|
take_profit: float
|
|
signal_type: str # description du setup
|
|
time: pd.Timestamp
|
|
order_block: Optional[OrderBlockZone] = None
|
|
liquidity_level: Optional[LiquidityLevel] = None
|
|
reason: Optional[TradeReason] = None
|
|
|
|
|
|
@dataclass
|
|
class AnalysisResult:
|
|
"""Résultat complet de l'analyse de la stratégie."""
|
|
order_blocks: list[OrderBlockZone] = field(default_factory=list)
|
|
liquidity_levels: list[LiquidityLevel] = field(default_factory=list)
|
|
signals: list[TradeSignal] = field(default_factory=list)
|
|
|
|
|
|
class AbstractStrategy(ABC):
|
|
"""Interface commune pour toutes les stratégies."""
|
|
|
|
@abstractmethod
|
|
def analyze(self, df: pd.DataFrame) -> AnalysisResult:
|
|
"""
|
|
Analyse un DataFrame de candles et retourne les zones,
|
|
niveaux de liquidité et signaux d'entrée.
|
|
|
|
df doit avoir les colonnes : time, open, high, low, close, volume
|
|
"""
|
|
...
|
|
|
|
@abstractmethod
|
|
def get_params(self) -> dict:
|
|
"""Retourne les paramètres configurables de la stratégie."""
|
|
...
|