@@ -20,9 +20,14 @@ class KellyResult:
2020
2121
2222_APPROVED_BOOTSTRAP_MANDATE = "bootstrap_small_account_v2"
23+ _TQQQ_ETF_ONLY_RESEARCH_MANDATE = "tqqq_etf_only_research_v1"
2324_BOOTSTRAP_LOSS_BUDGET_CAP = 0.01
2425_BOOTSTRAP_EFFECTIVE_EXPOSURE_CAP = 0.50
2526_BOOTSTRAP_NOMINAL_CAPS = {1 : 0.50 , 2 : 0.25 , 3 : 0.15 }
27+ _TQQQ_ETF_ONLY_PRODUCTS = {
28+ "TQQQ" : (3 , 0.15 , 0.45 ),
29+ "BOXX" : (1 , 0.50 , 0.50 ),
30+ }
2631
2732
2833def _weight_mapping (value : object , * , allow_empty : bool ) -> dict [str , float ] | None :
@@ -140,6 +145,7 @@ def validate_reduce_only_normalization(
140145 product_leverage_factors : Mapping [str , int ],
141146 effective_exposure_cap : float ,
142147 observed_effective_exposure : float ,
148+ cash_only : bool = False ,
143149) -> bool :
144150 """Validate one explicit transition from an over-cap origin toward cash."""
145151 origin = _weight_mapping (origin_weights , allow_empty = False )
@@ -153,6 +159,7 @@ def validate_reduce_only_normalization(
153159 or not isinstance (effective_exposure_cap , (int , float ))
154160 or isinstance (observed_effective_exposure , bool )
155161 or not isinstance (observed_effective_exposure , (int , float ))
162+ or not isinstance (cash_only , bool )
156163 ):
157164 return False
158165 cap = float (effective_exposure_cap )
@@ -178,6 +185,8 @@ def validate_reduce_only_normalization(
178185 target_active = {symbol for symbol , weight in target .items () if weight > 0.0 }
179186 if not origin_active or not target_active .issubset (origin_active ):
180187 return False
188+ if cash_only and target_active :
189+ return False
181190 if any (target .get (symbol , 0.0 ) > origin [symbol ] + 1e-9 for symbol in origin ):
182191 return False
183192 if any (
@@ -199,6 +208,7 @@ def validate_reduce_only_normalization(
199208def risk_budgeted_target_weight (
200209 * ,
201210 risk_mandate_id : str | None = None ,
211+ product_symbol : str | None = None ,
202212 account_equity : float | None = None ,
203213 risk_fraction : float | None = None ,
204214 stop_loss_distance : float | None = None ,
@@ -209,9 +219,9 @@ def risk_budgeted_target_weight(
209219) -> float :
210220 """Return a fail-closed single-account target weight.
211221
212- ``bootstrap_small_account_v2`` permits one ETF position with its approved
213- product cap. Without that mandate, the legacy 10% and unlevered fallback
214- applies. This helper does not allocate across strategies .
222+ Approved mandates permit one ETF position with their product caps. Without
223+ a mandate, the legacy 10% and unlevered fallback applies. This pure helper
224+ sizes a target; it does not authorize a product or execution .
215225 """
216226 numeric_inputs = (
217227 account_equity ,
@@ -256,18 +266,35 @@ def risk_budgeted_target_weight(
256266 nominal_cap = _BOOTSTRAP_NOMINAL_CAPS .get (product_leverage_factor , 0.0 )
257267 if nominal_cap == 0.0 :
258268 return 0.0
269+ elif risk_mandate_id == _TQQQ_ETF_ONLY_RESEARCH_MANDATE :
270+ product = _TQQQ_ETF_ONLY_PRODUCTS .get (product_symbol or "" )
271+ if (
272+ product is None
273+ or product_leverage_factor != product [0 ]
274+ or risk_fraction != _BOOTSTRAP_LOSS_BUDGET_CAP
275+ or stop_loss_distance != 0.05
276+ or drawdown_scalar not in {0.0 , 0.5 , 1.0 }
277+ ):
278+ return 0.0
279+ nominal_cap = product [1 ]
280+ product_effective_cap = product [2 ]
259281 else :
260282 return 0.0
261283
262284 risk_weight = risk_fraction * drawdown_scalar / stop_loss_distance
263285 if not math .isfinite (risk_weight ):
264286 return 0.0
265287
288+ effective_cap = (
289+ product_effective_cap
290+ if risk_mandate_id == _TQQQ_ETF_ONLY_RESEARCH_MANDATE
291+ else _BOOTSTRAP_EFFECTIVE_EXPOSURE_CAP
292+ )
266293 return min (
267294 risk_weight ,
268295 nominal_cap ,
269296 available_account_exposure ,
270- _BOOTSTRAP_EFFECTIVE_EXPOSURE_CAP / product_leverage_factor ,
297+ effective_cap / product_leverage_factor ,
271298 )
272299
273300
0 commit comments