Make risk guard symbol aware
This commit is contained in:
@@ -114,30 +114,39 @@ def risk_guard_snapshot(
|
||||
"position_size_multiplier": 1.0,
|
||||
"reasons": [],
|
||||
}
|
||||
active_trades = _active_universe_trades(settings, closed_trades)
|
||||
reasons: list[str] = []
|
||||
consecutive_losses = _consecutive_losses(closed_trades)
|
||||
degraded_reasons: list[str] = []
|
||||
consecutive_losses = _consecutive_losses(active_trades)
|
||||
if consecutive_losses >= settings.risk_max_consecutive_losses:
|
||||
reasons.append("consecutive_losses")
|
||||
today_pnl = _today_pnl(closed_trades)
|
||||
degraded_reasons.append("consecutive_losses")
|
||||
today_pnl = _today_pnl(active_trades)
|
||||
if today_pnl <= -abs(settings.max_daily_drawdown_usdt):
|
||||
reasons.append("daily_loss_limit")
|
||||
window = max(4, settings.risk_recent_trade_window)
|
||||
recent_stats = _trade_stats(closed_trades[:window])
|
||||
recent_stats = _trade_stats(active_trades[:window])
|
||||
if recent_stats["trades"] >= max(4, min(window, 8)):
|
||||
if recent_stats["profit_factor"] < settings.risk_min_recent_profit_factor:
|
||||
reasons.append("recent_profit_factor_below_min")
|
||||
degraded_reasons.append("recent_profit_factor_below_min")
|
||||
if recent_stats["avg_net_percent"] <= 0:
|
||||
reasons.append("recent_expectancy_non_positive")
|
||||
degraded_reasons.append("recent_expectancy_non_positive")
|
||||
latest_drawdown = float((latest_equity or {}).get("drawdown", 0.0) or 0.0)
|
||||
if latest_drawdown >= abs(settings.max_daily_drawdown_usdt):
|
||||
reasons.append("equity_drawdown_limit")
|
||||
block = bool({"consecutive_losses", "daily_loss_limit", "equity_drawdown_limit"} & set(reasons))
|
||||
multiplier = 0.0 if block else (settings.risk_reduce_multiplier if reasons else 1.0)
|
||||
symbol_stats = _symbol_guard_stats(settings, active_trades)
|
||||
blocked_symbols = sorted(row["symbol"] for row in symbol_stats if row["block_new_entries"])
|
||||
block = bool(reasons)
|
||||
all_reasons = reasons + degraded_reasons
|
||||
multiplier = 0.0 if block else (settings.risk_reduce_multiplier if degraded_reasons else 1.0)
|
||||
return {
|
||||
"enabled": True,
|
||||
"block_new_entries": block,
|
||||
"position_size_multiplier": round(max(0.0, min(1.0, multiplier)), 4),
|
||||
"reasons": reasons,
|
||||
"reasons": all_reasons,
|
||||
"global_reasons": reasons,
|
||||
"degraded_reasons": degraded_reasons,
|
||||
"blocked_symbols": blocked_symbols,
|
||||
"symbols": symbol_stats,
|
||||
"consecutive_losses": consecutive_losses,
|
||||
"today_pnl": round(today_pnl, 6),
|
||||
"recent": recent_stats,
|
||||
@@ -183,6 +192,41 @@ def _group_stats(trades: list[dict[str, Any]], key_fn) -> list[dict[str, Any]]:
|
||||
return sorted(rows, key=lambda row: (row["net_pnl"], row["trades"]), reverse=True)
|
||||
|
||||
|
||||
def _active_universe_trades(settings: Settings, trades: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
symbols = {symbol.upper() for symbol in settings.symbols}
|
||||
if not symbols:
|
||||
return trades
|
||||
return [trade for trade in trades if str(trade.get("symbol", "")).upper() in symbols]
|
||||
|
||||
|
||||
def _symbol_guard_stats(settings: Settings, trades: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
expectancy_min_samples = max(6, min(settings.risk_recent_trade_window, 10))
|
||||
loss_streak_min_samples = max(3, settings.risk_max_consecutive_losses)
|
||||
rows: list[dict[str, Any]] = []
|
||||
for symbol in settings.symbols:
|
||||
symbol_trades = [trade for trade in trades if str(trade.get("symbol", "")).upper() == symbol.upper()]
|
||||
recent = symbol_trades[: settings.risk_recent_trade_window]
|
||||
stats = _trade_stats(recent)
|
||||
losses = _consecutive_losses(recent)
|
||||
reasons: list[str] = []
|
||||
if stats["trades"] >= expectancy_min_samples:
|
||||
if stats["profit_factor"] < settings.risk_min_recent_profit_factor and stats["avg_net_percent"] <= 0:
|
||||
reasons.append("symbol_expectancy_negative")
|
||||
if stats["trades"] >= loss_streak_min_samples:
|
||||
if losses >= settings.risk_max_consecutive_losses:
|
||||
reasons.append("symbol_consecutive_losses")
|
||||
rows.append(
|
||||
{
|
||||
"symbol": symbol.upper(),
|
||||
"block_new_entries": bool(reasons),
|
||||
"reasons": reasons,
|
||||
"consecutive_losses": losses,
|
||||
**stats,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _trade_summary(trade: dict[str, Any]) -> dict[str, Any]:
|
||||
diagnostics = trade.get("entry_diagnostics") if isinstance(trade.get("entry_diagnostics"), dict) else {}
|
||||
forecast = diagnostics.get("forecast") if isinstance(diagnostics.get("forecast"), dict) else {}
|
||||
|
||||
Reference in New Issue
Block a user