GoshawkVortexAI commited on
Commit
fb4d43c
·
verified ·
1 Parent(s): ca6f944

Delete risk_engine.py

Browse files
Files changed (1) hide show
  1. risk_engine.py +0 -200
risk_engine.py DELETED
@@ -1,200 +0,0 @@
1
- """
2
- risk_engine.py — Adaptive risk management with consecutive-loss scaling,
3
- volatility-percentile-aware position sizing, and Kelly-influenced allocation.
4
-
5
- Key fixes vs prior version:
6
- - Consecutive loss counter drives a risk scale table (never compounds losses)
7
- - ATR stop multiplier is adaptive: widens in high-volatility to avoid noise stops
8
- - Position size caps at a hard notional limit regardless of risk fraction
9
- - Regime confidence feeds directly into risk fraction (low confidence = smaller size)
10
- - Separate max_drawdown_guard: if equity has drawn down >N% from peak, halt sizing
11
- """
12
-
13
- from typing import Dict, Any, List
14
-
15
- import numpy as np
16
-
17
- from config import (
18
- MAX_RISK_PER_TRADE,
19
- HIGH_VOL_THRESHOLD,
20
- LOW_VOL_THRESHOLD,
21
- REDUCED_RISK_FACTOR,
22
- ATR_STOP_MULT,
23
- RR_RATIO,
24
- DEFAULT_ACCOUNT_EQUITY,
25
- CONSEC_LOSS_RISK_SCALE,
26
- )
27
-
28
- _MAX_NOTIONAL_FRACTION = 0.30 # never put more than 30% of equity in one trade
29
- _MAX_DRAWDOWN_HALT = 0.15 # halt new positions if equity is down 15% from peak
30
- _ADAPTIVE_STOP_MULT_HIGH = 3.0 # wider stop when vol ratio > HIGH_VOL_THRESHOLD
31
- _ADAPTIVE_STOP_MULT_LOW = 2.0 # tighter stop when vol is compressed
32
-
33
-
34
- def adaptive_stop_multiplier(vol_ratio: float, compressed: bool) -> float:
35
- """
36
- Widen ATR stop in high volatility to avoid noise-out.
37
- Use tighter stop when entering from a compressed base (cleaner structure).
38
- """
39
- if vol_ratio > HIGH_VOL_THRESHOLD:
40
- return _ADAPTIVE_STOP_MULT_HIGH
41
- if compressed:
42
- return _ADAPTIVE_STOP_MULT_LOW
43
- return ATR_STOP_MULT
44
-
45
-
46
- def consecutive_loss_scale(consec_losses: int) -> float:
47
- """
48
- Step-down risk table — each loss reduces risk fraction.
49
- Prevents geometric compounding of losses during drawdown streaks.
50
- Table is defined in config.CONSEC_LOSS_RISK_SCALE.
51
- """
52
- idx = min(consec_losses, len(CONSEC_LOSS_RISK_SCALE) - 1)
53
- return CONSEC_LOSS_RISK_SCALE[idx]
54
-
55
-
56
- def compute_dynamic_risk_fraction(
57
- vol_ratio: float,
58
- regime_score: float,
59
- volume_score: float,
60
- regime_confidence: float,
61
- consec_losses: int = 0,
62
- equity_drawdown_pct: float = 0.0,
63
- base_risk: float = MAX_RISK_PER_TRADE,
64
- ) -> float:
65
- """
66
- Multi-factor risk fraction with hard halt on drawdown breach.
67
-
68
- Priority order (each multiplies, not adds):
69
- 1. Drawdown guard (hard gate)
70
- 2. Consecutive loss scale
71
- 3. Volatility regime adjustment
72
- 4. Regime score quality
73
- 5. Confidence floor
74
- """
75
- # Hard halt: equity drawn down too far from peak
76
- if equity_drawdown_pct >= _MAX_DRAWDOWN_HALT:
77
- return 0.0
78
-
79
- risk = base_risk
80
-
81
- # Consecutive loss scaling
82
- risk *= consecutive_loss_scale(consec_losses)
83
-
84
- # Volatility adjustment
85
- if vol_ratio > HIGH_VOL_THRESHOLD:
86
- risk *= REDUCED_RISK_FACTOR
87
- elif vol_ratio > HIGH_VOL_THRESHOLD * 0.75:
88
- risk *= 0.70
89
- elif vol_ratio < LOW_VOL_THRESHOLD:
90
- risk *= 0.80 # also reduce in extreme low vol (thin market)
91
-
92
- # Regime quality
93
- if regime_score < 0.25:
94
- risk *= REDUCED_RISK_FACTOR
95
- elif regime_score < 0.45:
96
- risk *= 0.65
97
- elif regime_score < 0.60:
98
- risk *= 0.85
99
-
100
- # Confidence gate: confidence below threshold scales linearly to zero
101
- if regime_confidence < 0.30:
102
- risk *= 0.25
103
- elif regime_confidence < 0.55:
104
- risk *= regime_confidence # proportional scaling
105
-
106
- return float(np.clip(risk, 0.001, base_risk))
107
-
108
-
109
- def compute_position_size(
110
- account_equity: float,
111
- entry_price: float,
112
- stop_distance: float,
113
- risk_fraction: float,
114
- ) -> float:
115
- if stop_distance <= 0 or entry_price <= 0 or account_equity <= 0:
116
- return 0.0
117
- dollar_risk = account_equity * risk_fraction
118
- units = dollar_risk / stop_distance
119
- notional = units * entry_price
120
- # Hard cap: never exceed _MAX_NOTIONAL_FRACTION of equity in one trade
121
- max_notional = account_equity * _MAX_NOTIONAL_FRACTION
122
- return float(min(notional, max_notional))
123
-
124
-
125
- def evaluate_risk(
126
- close: float,
127
- atr: float,
128
- atr_pct: float,
129
- regime_score: float,
130
- vol_ratio: float,
131
- volume_score: float = 0.5,
132
- regime_confidence: float = 0.5,
133
- vol_compressed: bool = False,
134
- consec_losses: int = 0,
135
- equity_drawdown_pct: float = 0.0,
136
- account_equity: float = DEFAULT_ACCOUNT_EQUITY,
137
- rr_ratio: float = RR_RATIO,
138
- ) -> Dict[str, Any]:
139
- stop_mult = adaptive_stop_multiplier(vol_ratio, vol_compressed)
140
- stop_distance = atr * stop_mult
141
-
142
- risk_fraction = compute_dynamic_risk_fraction(
143
- vol_ratio=vol_ratio,
144
- regime_score=regime_score,
145
- volume_score=volume_score,
146
- regime_confidence=regime_confidence,
147
- consec_losses=consec_losses,
148
- equity_drawdown_pct=equity_drawdown_pct,
149
- base_risk=MAX_RISK_PER_TRADE,
150
- )
151
-
152
- position_notional = compute_position_size(
153
- account_equity=account_equity,
154
- entry_price=close,
155
- stop_distance=stop_distance,
156
- risk_fraction=risk_fraction,
157
- )
158
-
159
- dollar_at_risk = account_equity * risk_fraction
160
- reward_distance = stop_distance * rr_ratio
161
- leverage_implied = position_notional / account_equity if account_equity > 0 else 0.0
162
-
163
- # Risk quality: composite readiness score
164
- quality = 1.0
165
- if vol_ratio > HIGH_VOL_THRESHOLD:
166
- quality -= 0.25
167
- if regime_score < 0.40:
168
- quality -= 0.20
169
- if regime_confidence < 0.55:
170
- quality -= 0.15
171
- if consec_losses >= 2:
172
- quality -= 0.15
173
- risk_quality = float(np.clip(quality, 0.0, 1.0))
174
-
175
- halted = equity_drawdown_pct >= _MAX_DRAWDOWN_HALT
176
-
177
- return {
178
- "entry_price": close,
179
- "atr": round(atr, 8),
180
- "atr_pct": round(atr_pct * 100, 3),
181
- "stop_mult": round(stop_mult, 2),
182
- "stop_distance": round(stop_distance, 8),
183
- "stop_long": round(close - stop_distance, 8),
184
- "stop_short": round(close + stop_distance, 8),
185
- "target_long": round(close + reward_distance, 8),
186
- "target_short": round(close - reward_distance, 8),
187
- "reward_distance": round(reward_distance, 8),
188
- "rr_ratio": rr_ratio,
189
- "risk_fraction": round(risk_fraction * 100, 4),
190
- "dollar_at_risk": round(dollar_at_risk, 2),
191
- "position_notional": round(position_notional, 2),
192
- "leverage_implied": round(leverage_implied, 3),
193
- "vol_ratio": round(vol_ratio, 3),
194
- "regime_score": round(regime_score, 4),
195
- "regime_confidence": round(regime_confidence, 4),
196
- "consec_losses": consec_losses,
197
- "equity_drawdown_pct": round(equity_drawdown_pct * 100, 2),
198
- "risk_quality": round(risk_quality, 3),
199
- "sizing_halted": halted,
200
- }