Skip to content

Add return_raw option to BlackLittermanModel.bl_weights - #764

Open
heykav wants to merge 1 commit into
PyPortfolio:mainfrom
heykav:feat/bl-weights-return-raw
Open

heykav wants to merge 1 commit into
PyPortfolio:mainfrom
heykav:feat/bl-weights-return-raw

Conversation

@heykav

@heykav heykav commented Sep 21, 2026

Copy link
Copy Markdown

Closes #763.

What

bl_weights() normalizes the solved weights to sum to 1 before returning them. That's the right default, but it discards information: the raw (pre-normalization) solution's total encodes how much net borrowing/lending the posterior implies, and there's no way to recover it once it's been normalized away.

Adds return_raw=False to bl_weights(). When True, it returns the raw weights through the same _make_output_weights() path instead of the normalized ones.

Why this is safe

self.weights is always set to the normalized version regardless of the flag — only the return value changes. optimize() (the alias) and every other caller that reads self.weights afterward (e.g. portfolio_performance()) sees identical behavior to before this change.

Verification

  • New regression test test_bl_weights_return_raw: confirms the raw weights are proportional to the normalized weights by exactly the sum that normalization divided out, confirms self.weights stays normalized, and confirms the default (return_raw=False) call is byte-identical to pre-change behavior.
  • Full suite: 280 passed, 32 skipped. The one failure in test_hrp.py::test_hrp_errors is the pre-existing, already-tracked scipy._LINKAGE_METHODS incompatibility (HRPOpt fails with current SciPy because _LINKAGE_METHODS was removed #754, already has an open PR at Fix HRP SciPy linkage method validation #755) — unrelated to this change, reproduces identically on main without it.
  • ruff check and black --check clean on both changed files.

Closes PyPortfolio#763. bl_weights() normalizes the solved weights to sum to 1
before returning them, which discards the total leverage information
in the raw (pre-normalization) solution - there's no way to recover
how much borrowing/lending the posterior implied once it's normalized
away. Adds a return_raw=False parameter; when True, returns the raw
weights via the same _make_output_weights() path instead. self.weights
is always set to the normalized version regardless of the flag, so
portfolio_performance() and every other existing caller is unaffected.

Verified: full test suite passes (280 passed, 32 skipped; the one
pre-existing HRPOpt/scipy failure in test_hrp.py is unrelated and
already tracked in PyPortfolio#754/PyPortfolio#755). ruff and black clean on both changed
files.
@heykav

heykav commented Sep 21, 2026

Copy link
Copy Markdown
Author

Noting for the reviewer: @jrvarma suggested on #763 that this might read better as normalize=True (returning raw weights when False) rather than return_raw=False. Both are reasonable — there's no existing normalize= convention elsewhere in this codebase to anchor to, so I went with the more literal return_raw naming, but happy to switch if that's the preferred convention here. Let me know either way and I'll update it in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Black Litterman function bl_weights should have option to return raw_weights

1 participant