deploy: 2026-03-20 07:49
This commit is contained in:
67
risk/drawdown_monitor.py
Normal file
67
risk/drawdown_monitor.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Drawdown monitoring and equity curve tracking."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
@dataclass
|
||||
class DrawdownState:
|
||||
"""Current drawdown statistics."""
|
||||
|
||||
current_drawdown: float = 0.0
|
||||
max_drawdown: float = 0.0
|
||||
peak_equity: float = 0.0
|
||||
trough_equity: float = 0.0
|
||||
is_in_drawdown: bool = False
|
||||
|
||||
|
||||
class DrawdownMonitor:
|
||||
"""Track equity curve and compute drawdown metrics."""
|
||||
|
||||
def __init__(self, max_drawdown_limit: float = 0.15):
|
||||
self.limit = max_drawdown_limit
|
||||
self._equity_curve: List[float] = []
|
||||
self._peak: float = 0.0
|
||||
self._max_dd: float = 0.0
|
||||
|
||||
def update(self, equity: float) -> DrawdownState:
|
||||
"""Record new equity point and recalculate drawdown."""
|
||||
self._equity_curve.append(equity)
|
||||
|
||||
if equity > self._peak:
|
||||
self._peak = equity
|
||||
|
||||
current_dd = (self._peak - equity) / self._peak if self._peak > 0 else 0.0
|
||||
if current_dd > self._max_dd:
|
||||
self._max_dd = current_dd
|
||||
|
||||
state = DrawdownState(
|
||||
current_drawdown=current_dd,
|
||||
max_drawdown=self._max_dd,
|
||||
peak_equity=self._peak,
|
||||
trough_equity=min(self._equity_curve) if self._equity_curve else 0.0,
|
||||
is_in_drawdown=current_dd > 0,
|
||||
)
|
||||
|
||||
if current_dd >= self.limit:
|
||||
logger.warning(
|
||||
"DRAWDOWN ALERT: {:.2%} >= limit {:.2%}", current_dd, self.limit
|
||||
)
|
||||
|
||||
return state
|
||||
|
||||
@property
|
||||
def equity_curve(self) -> List[float]:
|
||||
return list(self._equity_curve)
|
||||
|
||||
@property
|
||||
def max_drawdown(self) -> float:
|
||||
return self._max_dd
|
||||
|
||||
def is_breached(self) -> bool:
|
||||
"""Return True if max drawdown limit has been exceeded."""
|
||||
return self._max_dd >= self.limit
|
||||
Reference in New Issue
Block a user