@@ -74,6 +74,14 @@ def rolling_risk_budgeting(prices: pd.DataFrame,
7474 Returns:
7575 DataFrame of portfolio weights.
7676 """
77+ # Single-asset universe: trivial 100% allocation at every rebalancing date.
78+ if len (risk_budget ) == 1 :
79+ asset = risk_budget .index [0 ]
80+ weights = pd .DataFrame (1.0 ,
81+ index = pd .DatetimeIndex (list (covar_dict .keys ())),
82+ columns = [asset ])
83+ return weights .reindex (columns = prices .columns .to_list ()).fillna (0.0 )
84+
7785 if rebalancing_indicators is not None :
7886 rebalancing_dates = list (covar_dict .keys ())
7987 rebalancing_indicators = rebalancing_indicators .reindex (index = rebalancing_dates ).fillna (0.0 )
@@ -313,7 +321,6 @@ def risk_budget_objective(x, pars) -> float:
313321
314322def solve_for_risk_budgets_from_given_weights (prices : pd .DataFrame ,
315323 given_weights : pd .Series ,
316- time_period : qis .TimePeriod ,
317324 covar_dict : Dict [pd .Timestamp , pd .DataFrame ],
318325 min_risk_budget : float = 1e-4 ,
319326 max_risk_budget : float = 0.99
@@ -324,14 +331,19 @@ def solve_for_risk_budgets_from_given_weights(prices: pd.DataFrame,
324331 Args:
325332 prices: Asset price panel.
326333 given_weights: Target portfolio weights to reproduce.
327- time_period: Period for rolling backtest within the objective function.
328334 covar_dict: Pre-computed covariance matrices.
329335 min_risk_budget: Lower bound on each non-zero risk budget.
330336 max_risk_budget: Upper bound on each risk budget.
331337
332338 Returns:
333339 Optimal risk budgets as pd.Series. Budgets sum to 1.
334340 """
341+ # Single-asset universe: the only budget consistent with sum=1 is 1.0
342+ # on the lone asset. Skip the solver — it would be infeasible under the
343+ # max_risk_budget=0.99 cap anyway.
344+ if prices .shape [1 ] == 1 :
345+ return pd .Series (1.0 , index = prices .columns )
346+
335347 given_weights_np = given_weights .to_numpy ()
336348
337349 def objective_function (risk_budgets : np .ndarray ) -> float :
@@ -371,4 +383,4 @@ def objective_function(risk_budgets: np.ndarray) -> float:
371383 warnings .warn (f"solve_for_risk_budgets_from_given_weights: solver failed, using zero budgets" )
372384 risk_budgets = np .zeros_like (x0 )
373385 risk_budgets = pd .Series (risk_budgets , index = prices .columns )
374- return risk_budgets
386+ return risk_budgets
0 commit comments