The Learning Library
Contents

Module 25 — Portfolio Construction & Strategy Combination

Part V · Production · Priority ⭐ Recommended · Status: Draft v0.1 Prerequisites: Module 5, Module 16, Module 19, Module 22, Module 23


Overview

Portfolio construction combines several individually imperfect strategies into one book whose equity curve is smoother, deeper-drawdown-proof, and longer-lived than any single sleeve. The edge is not prediction — it is arithmetic: a portfolio of weakly-correlated mediocre edges beats one strong concentrated edge on every risk-adjusted measure that matters for survival (Sharpe, drawdown depth, recovery time, capacity). Your counterparty here is not another trader but your own estimation error: correlations, volatilities, and capacities are all noisy measurements, and naive optimizers turn that noise into confident-looking bad allocations.

This module is the capstone of the curriculum. Everything before it built components — signals, validation, sizing, execution, costs. This module decides how much capital each component gets, which is frequently worth more than improving any one component. Treat every allocation decision with the same validation discipline as a strategy decision, because it is one.

House position: start boring (equal-weight), upgrade honestly (inverse-vol), reach for hierarchy-aware methods (HRP) only when the book grows enough for allocation quality to bind.


How It Works

The portfolio is a loop, not a one-time decision. Sleeve ledgers feed correlation analysis; analysis feeds an allocation method; allocations pass through the per-sleeve sizing overlay; combined-book telemetry feeds drift monitors; drift findings trigger throttles and the periodic re-allocation review, which starts the loop again with fresh estimates.

Figure: the portfolio assembly loop — money flows down the left side, information flows back up the right side.

flowchart TD
    ledgers[(Per-sleeve journals<br/>one magic per strategy)]
    corr[Calm-vs-crash correlation<br/>plus overlap scan]
    alloc{Allocation method<br/>equal / inv-vol / HRP}
    overlay[Sizing overlay<br/>M23 ladder per sleeve]
    book[(Combined-book equity<br/>telemetry and TCA feed)]
    drift{Drift or DD breach?}
    steady[Keep trading<br/>log everything]
    derisk[Throttle offending sleeve<br/>via ladder tier]
    review[Quarterly reallocation<br/>review]

    ledgers --> corr
    corr -->|"sleeve statistics"| alloc
    alloc -->|"target weights"| overlay
    overlay -->|"orders"| book
    book --> drift
    drift -->|"no"| steady
    steady -->|"quarterly"| review
    drift -->|"yes"| derisk
    derisk --> review
    review -->|"fresh estimates"| corr

    classDef data fill:#e8f0fe,stroke:#4a86e8
    classDef process fill:#f3f3f3,stroke:#888
    classDef decision fill:#fff4d6,stroke:#d6a300
    classDef risk fill:#fde8e8,stroke:#c0392b

    class ledgers,book data
    class corr,overlay,steady,review process
    class alloc,drift decision
    class derisk risk

How to read this:

  • The blue nodes are artifacts you already own (Module 24 journals); everything else is analysis and policy layered on top.
  • The red path is the emergency lane: a breached monitor throttles the offending sleeve through the Module 23 ladder, it does not re-optimize mid-crisis.
  • The bottom-left cycle closes only at the quarterly review — intra-period, weights are frozen except for band breaches, because churn costs money (Module 22).

Strategy Correlation Analysis

Correlation between sleeves is the raw material of every allocation decision, and it is the number most likely to lie to you. Three analyses, all mandatory before any optimizer runs.

Calm versus crash behavior

Pairwise return correlations measured on a calm window systematically understate what you experience in stress — correlations spike exactly when diversification is needed. Module 16 measured this at asset level: mean pairwise correlation of +0.21 in a quiet bull window versus +0.68 in the COVID crash window. Sleeve level behaves the same way: an M5 mean-reverter and a D1 trend follower that look beautifully uncorrelated across three years can converge in the fortnight when the VIX doubles, because both end up long the same liquidity withdrawal.

Judge every diversification claim against the crash-window number. If two sleeves correlate above roughly +0.5 in the crash window, treat them as one sleeve wearing two names for sizing purposes — combine them before allocating, or you will discover the merger at the worst possible moment.

# Sleeve co-movement dashboard: mean pairwise P&L correlation, calm vs crash.
# `daily_pnl` is a pandas frame (brevity here; port to Polars in production)
# indexed by date, one column per strategy sleeve.
import numpy as np

def mean_pairwise_corr(window_pnl):
    corr_matrix = np.corrcoef(window_pnl.to_numpy(), rowvar=False)
    off_diagonal = corr_matrix[~np.eye(corr_matrix.shape[0], dtype=bool)]
    return float(off_diagonal.mean())

def sleeve_correlation_report(daily_pnl, sleeves, calm, crash, lookback=63):
    windows = {
        "calm":    daily_pnl.loc[calm[0]:calm[1]],
        "crash":   daily_pnl.loc[crash[0]:crash[1]],
        "current": daily_pnl.tail(lookback),
    }
    return {label: round(mean_pairwise_corr(w[sleeves]), 2)
            for label, w in windows.items()}

# -> {'calm': 0.21, 'crash': 0.68, 'current': 0.34}
# The crash number is the binding constraint; the calm number is marketing.

Overlap detection via trade-timestamp coincidence

Return correlations can miss overlap when sleeves trade at different frequencies. The direct test: count how often two sleeves open positions in the same symbol within the same hour (or session). Two systems that repeatedly click into the same market in the same direction within minutes are one bet funded twice, regardless of what their daily P&L correlation claims. Log coincidence rates alongside the correlation dashboard; anything above chance gets a manual autopsy before both sleeves keep full capital.

Regime co-crash matrix

Extend the calm-vs-crash comparison into a full matrix: label each historical day with a regime (your HMM states from Modules 15/17), then compute mean pairwise sleeve correlation per regime. Rows are regimes, columns are sleeve pairs, cells are correlations. Any regime where the average cell exceeds ~+0.5 is named on the dashboard as a “convergence state” — those are the periods your diversification story must explicitly survive, and the quarterly review reads this matrix first.


Allocation Methods Compared

Each method answers one question — how should the next dollar of risk be split? — with different trust in its own inputs.

Method Rule in plain words Trust level Failure mode
Equal-weight Same capital to every sleeve Ignores data entirely Vol-heavy sleeve dominates book risk
Inverse-vol Weight proportional to 1/volatility Only trusts vol estimates Ignores correlation structure
Risk parity Budget equal risk per sleeve, iteratively Vol + correlations Sensitive to correlation noise
Min-variance Weights minimizing portfolio variance Full covariance matrix Error maximizer; corner solutions
HRP Cluster sleeves, bisect hierarchically Structure, not precision Slower to reward a genuinely great sleeve

Equal-weight is the honest default: zero estimation input, trivially explainable, and the benchmark every fancier scheme must beat out-of-sample. Inverse-vol is usually the first real upgrade — it stops a 40%-vol crypto sleeve from eating half the book’s risk budget while holding a third of its capital. Risk parity generalizes inverse-vol to account for correlations, spending equal variance budget on each sleeve; with few sleeves it rarely differs much from inverse-vol. Min-variance solves for the lowest-variance mix given the full covariance matrix — mathematically elegant and empirically treacherous (see estimation error below). Black–Litterman, mentioned once for completeness, blends investor views with market-equilibrium weights to tame mean-variance’s input sensitivity; it presumes views worth blending, which most retail books do not yet have.

Hierarchical Risk Parity (HRP) — López de Prado (2016) — attacks the estimation problem structurally: it builds a tree of sleeves using correlation distance, then splits capital down that tree by recursive bisection. Because it never inverts the covariance matrix, tiny estimation errors cannot be amplified into precise-looking poor allocations — the failure mode where mean-variance earns its “error maximizer” nickname. HRP produces many small, stable, long-only weights and handles more sleeves than observations gracefully.

# Hierarchical Risk Parity sketch -- PyPortfolioOpt implementation (labeled).
# Under the hood: tree clustering on correlation distance + recursive bisection.
# NO covariance inversion anywhere, so noisy inputs yield stable, sane weights.
from pypfopt import HRPOpt, risk_models

returns_frame = combined_sleeve_returns        # wide frame: dates x sleeve returns
hrp = HRPOpt(returns=returns_frame)
hrp_weights = hrp.optimize()
print(hrp.clean_weights())
# -> e.g. {'trend_d1': 0.31, 'reverter_m5': 0.28, 'event': 0.22, 'btc_trend': 0.19}
# Stability test: refit monthly and diff weights. HRP typically turns over far
# less than mean-variance on identical inputs -- that stability IS its edge.

# Contrast: min-variance inverts the (shrunken) covariance -- handle with care:
shrunk_cov = risk_models.CovarianceShrinkage(
    returns_frame).ledoit_wolf()               # shrinkage pulls eigenvalues in

🧪 Evidence: an out-of-sample study by PortfoliosLab (mvo-vs-hrp-out-of-sample) found that classical minimum-volatility mean-variance optimization produced lower realized volatility than HRP in ~92% of quarterly-rebalanced portfolios tested. Read both sides honestly: HRP’s documented advantage is the robustness and stability of its weights, not a guarantee of lower volatility. On small retail universes, a well-shrunk min-variance can win on realized vol; HRP wins when input noise, regime shifts, or many similar sleeves would otherwise wreck the optimizer.

Estimation-error humility

Why do fancy optimizers disappoint out of sample? Because they are error maximizers: an optimizer loads maximum weight on whichever sleeve happened to have the luckiest historical return and lowest measured variance — precisely the inputs carrying the largest estimation error. With 250 days of history, a sleeve’s Sharpe estimate carries a confidence interval wider than the difference between a good and bad sleeve; the optimizer cannot see the interval and treats noise as signal. The out-of-sample result is routinely worse than the naive scheme it was supposed to improve on.

Shrinkage is the standard humility device: blend the sample covariance matrix toward a structured, low-variance target (a diagonal matrix, or a constant-correlation matrix) — Ledoit–Wolf does this automatically and is one line in the snippet above. Intuition: you are averaging your noisy measurement with a stable prior, exactly as you’d haircut a Kelly fraction in Module 23. Weight bands and caps (below) are shrinkage in disguise — a hard refusal to let any estimate move the book too far.

Worked example: inverse-vol by hand

Three sleeves: D1 trend (8% annualized vol), M5 reverter (12%), event system (20%).

Raw inverse vols : 1/8 = 0.12500   1/12 = 0.08333   1/20 = 0.05000
Divisor (sum)    : 0.12500 + 0.08333 + 0.05000 = 0.25833

Weights          : 0.12500/0.25833 = 48.39%
                   0.08333/0.25833 = 32.26%
                   0.05000/0.25833 = 19.35%       (sum = 100.00%)

Risk contribution per sleeve = weight x vol:
  trend D1     0.48387 x 8%  = 3.87%
  reverter M5  0.32258 x 12% = 3.87%
  event        0.19355 x 20% = 3.87%   <- equal slices: inverse-vol IS equal risk

With zero correlation between sleeves, book volatility is the square root of summed squared contributions: sqrt(3 × 3.87²) = 6.70% — less than the least volatile sleeve alone. Now hold the weights and apply the Module 16 crash number, +0.68 correlation between every pair: each unordered pair adds 2 × 0.68 × 3.87 × 3.87 to variance. Total variance = 44.95 + 6 × 0.68 × 14.98 = 44.95 + 61.11 = 106.06, so book vol rises to ≈ 10.3%. Same weights, same sleeves — the crash environment alone erases a third of the diversification benefit. This is why the correlation dashboard watches crash windows continuously.

# Inverse-volatility allocator: weight each sleeve by 1 / its return volatility.
# The simplest honest upgrade over static caps when vols differ by 2x or more.
import numpy as np

def inverse_vol_weights(sleeve_vol: dict[str, float]) -> dict[str, float]:
    raw = {name: 1.0 / vol for name, vol in sleeve_vol.items()}
    total = sum(raw.values())
    return {name: w / total for name, w in raw.items()}

sleeve_vols = {"trend_d1": 0.08, "reverter_m5": 0.12, "event": 0.20}
weights = inverse_vol_weights(sleeve_vols)
# -> {'trend_d1': 0.484, 'reverter_m5': 0.323, 'event': 0.194}

# Sanity check: with uncorrelated sleeves every sleeve contributes the SAME
# volatility slice -- equal risk from unequal capital is the entire point.
for name, weight in sorted(weights.items()):
    print(f"{name}: contributes {100 * weight * sleeve_vols[name]:.2f}% vol")
# trend_d1: 3.87 | reverter_m5: 3.87 | event: 3.87

Multi-Strategy Execution on One MT5 Account

One account running several strategies is operationally simpler and audit-friendly, provided segregation is enforced mechanically.

Magic-number segregation. Every order carries a magic number unique to its strategy-symbol pair, hardcoded from config (Module 24). Reconciliation, kill-switch actions, and per-sleeve P&L attribution all filter on this key; a collision silently corrupts the close logic, so the config loader rejects duplicates at startup.

Margin budgeting under leverage. Each sleeve consumes margin independently; the binding constraint is total used margin versus free-margin buffer, checked at order time by the supervisor. Budget conservatively: sum each sleeve’s peak concurrent margin (not its average), add a stress multiplier for spread-widening episodes, and refuse new entries that push total margin above the ceiling — a margin call is the one failure mode no backtest forgives.

Netting versus hedging account modes. On a netting account (Module 1), opposite positions in the same symbol merge into one net position — if the trend sleeve is long 2 lots and the reverter goes short 1, the broker sees one 1-lot long, and a reverter stop-out closes part of the net position, not the reverter’s leg. Per-sleeve exit logic breaks silently. On a hedging account the legs stay separate and magic-number accounting works cleanly, at the price of double margin/swap bookkeeping. Confirm the mode with the broker before combining sleeves; if forced onto netting, separate conflicting sleeves onto different symbols or accounts.

# What changes in practice: segregation lives in config, loaded and validated
# at startup. Caps come from the capacity study, not from hope.
[supervisor]
account_mode = "hedging"        # confirm with broker BEFORE combining sleeves
max_total_margin_pct = 35       # hard ceiling on summed sleeve margin use

[[sleeve]]
name = "xau_reverter_r0"
magic = 860001
capacity_cap = 24               # units; summed caps = 68 across the book
weight_band = [0.36, 0.60]      # act only outside the band

[[sleeve]]
name = "btc_trend_r2"
magic = 860002
capacity_cap = 10
weight_band = [0.10, 0.30]

[[sleeve]]
name = "xau_trend_d1"
magic = 860003
capacity_cap = 34
weight_band = [0.20, 0.45]

Rebalancing Policy & Turnover Budgets

Weights decay as volatilities and correlations drift; rebalancing restores targets at a cost. The policy question is when to bother.

Trigger Rule example Turnover profile Best for
Calendar Rebalance quarterly, fixed date Lumpy, predictable Slow, stable sleeves
Threshold bands Act only when a weight leaves ±25% (relative) Self-limiting Volatile sleeves
Hybrid Quarterly check OR band breach Balanced Most real books

Bands deserve emphasis: they make turnover endogenous. A weight that stays near target costs nothing; only genuine drift pays the toll. Set bands relative (target ±25% of itself) rather than absolute, so a 5% sleeve and a 50% sleeve get proportionate freedom.

Every rebalance books its costs explicitly: estimated spread + commission + impact per leg, summed into a rolling turnover ledger that feeds the Module 22 TCA loop. If annual rebalancing costs exceed roughly a tenth of the diversification benefit gained (measured as book-vol reduction versus weighted-average sleeve vol), the policy is too chatty — widen the bands.

Capacity planning spans the combined book: summed sleeve capacities are an upper bound, not a promise. Two sleeves holding correlated positions in the same instrument share one exit door; their effective joint capacity is lower than the sum of solo capacities. Derate overlapping pairs using the crash-window correlation from the dashboard — the higher the crash correlation, the larger the haircut.


Monitoring Drift at Book Level

Individual sleeve health checks are necessary but insufficient: the combination drifts in ways no sleeve notices.

  • Factor-exposure creep. Several sleeves independently scaling into the same direction can leave the book implicitly long one factor (gold beta, crypto beta, short-vol) without any single sleeve exceeding its limits. Track rolling book-level factor proxies (e.g., book P&L regressed on benchmark moves) and alarm when the implied exposure crosses its band.
  • Hidden leverage accumulation. Vol-targeted sleeves scale up as realized vol falls; a quiet quarter silently gears the whole book. Monitor summed notional-to-equity and predicted book vol as first-class metrics, not just realized P&L.
  • Strategy-level health dashboards. Reuse the Module 19 drift machinery — PSI/KS tests — directly on sleeve-level streams: feature distributions, signal frequencies, fill rates, P&L distributions versus validation reference windows. A sleeve drifting from its audited self is a candidate for throttle even if it is profitable.
  • Quarterly re-validation ritual. Once per quarter: refresh the correlation/co-crash matrices, re-run each sleeve’s entry criteria (its validation-ladder summary metrics), re-fit allocation inputs, and write the decisions down — promote, hold, throttle, retire. Un-reviewed combinations rot; the calendar entry is the control.
# Combined-book drawdown monitor: WHO is causing the pain right now?
# Builds a per-sleeve attribution table for the current drawdown episode.
import pandas as pd

def drawdown_attribution(book_equity: pd.Series, sleeve_pnl: pd.DataFrame):
    peak = book_equity.cummax()
    drawdown = book_equity - peak
    trough_date = drawdown.idxmin()
    episode_start = book_equity.loc[:trough_date].idxmax()  # last peak pre-trough
    window = sleeve_pnl.loc[episode_start:trough_date]
    total_loss = drawdown.min()
    rows = [{
        "sleeve": sleeve,
        "pnl_in_drawdown": round(float(window[sleeve].sum())),
        "share_of_dd": f"{100 * window[sleeve].sum() / total_loss:.0f}%",
    } for sleeve in window.columns]
    return pd.DataFrame(rows).sort_values("pnl_in_drawdown")

# ->      sleeve  pnl_in_drawdown share_of_dd
# ->       event          -18400         62%   <- throttle THIS one first
# ->    reverter           -7900         27%
# ->     trend_d1           -3200         11%   (trend often pays during crashes)

🔧 For your pipeline: you already run R0/R2 route books on XAUUSD + BTCUSD through one supervisor with explicit summed capacities (68 total). Concretely: (1) treat the route-asset pairs as four sleeves — XAU-R0, BTC-R0, XAU-R2, BTC-R2 — and split the journal P&L stream by magic accordingly; (2) estimate the 4×4 sleeve correlation matrix from journal daily P&L, including a declared crash window, replacing today’s assumption-free static caps; (3) implement inverse-vol allocation first — the simplest honest upgrade over fixed capacity sums — behind weight bands; (4) simulate the combined-book equity under inverse-vol versus current fixed-capacity behavior over the same history and compare drawdown profiles; (5) note that your BTCUSD sleeve’s −92% drawdown history argues for vol-scaled allocation before anything fancier — a 20%-vol-class sleeve must not receive equal-weight capital (Module 23’s ladder applies per sleeve inside the book).


Testing It Honestly

Allocation research inherits every Module 19/Module 5 rule. The extra traps are specific to combination.

Fixture with known truth. Synthetic sleeves with a known correlation let you assert the allocator recovers what theory says, instead of eyeballing outputs.

# Fixture: synthetic sleeves with KNOWN correlation, so allocator tests can
# assert against ground truth instead of vibes.
import numpy as np

def synthetic_sleeves(n_days=750, true_corr=0.6, seed=42):
    rng = np.random.default_rng(seed)
    common_factor = rng.normal(size=n_days)              # shared market driver
    idio_a, idio_b = rng.normal(size=n_days), rng.normal(size=n_days)
    rho = true_corr
    sleeve_a = np.sqrt(rho) * common_factor + np.sqrt(1 - rho) * idio_a
    sleeve_b = np.sqrt(rho) * common_factor + np.sqrt(1 - rho) * idio_b
    return sleeve_a * 0.0008, sleeve_b * 0.0012          # realistic daily vols

daily_a, daily_b = synthetic_sleeves()
measured = np.corrcoef(daily_a, daily_b)[0, 1]
assert abs(measured - 0.60) < 0.05                       # -> passes

Allocation stability metric. Report weight turnover per rebalance for every scheme you test — a scheme whose weights swing wildly on stationary inputs is amplifying noise, whatever its backtest Sharpe claims.

# Allocation stability: one-way weight turnover per rebalance.
def weight_turnover(old_weights: dict, new_weights: dict) -> float:
    names = set(old_weights) | set(new_weights)
    return 0.5 * sum(abs(new_weights.get(n, 0.0) - old_weights.get(n, 0.0))
                     for n in names)

# A quarterly-rebalanced book turning over >20% per quarter is a red flag:
# either the estimator is too noisy or the rebalancer overreacts to it.

Walk-forward only. Choosing among allocation methods, tuning band widths, or fitting shrinkage intensities on the same window you evaluate is the same sin as tuning a strategy on its test set. Fit allocation inputs on training folds, score out-of-fold, respect an untouched holdout, and compare schemes on net-of-cost equity curves including crash windows — a scheme that wins by 0.1 Sharpe in calm years and loses 30% deeper in the crash year lost.


Hands-On Project

Capstone deliverable: a running multi-strategy demo book with full telemetry, a comparison memo (docs/research/capstone_combined_book.md), and the operations runbook.

Tasks:

  1. Assemble 3–4 systems that individually passed their validation ladders, spanning frequency rungs and assets (e.g., D1 trend on XAUUSD, M5 reverter, event/session system, BTC trend).
  2. Build per-sleeve journals keyed by magic number; produce the correlation dashboard with calm, crash, and current numbers plus the trade-overlap scan; merge or demote any pair with high co-crash correlation.
  3. Implement equal-weight, inverse-vol, and one hierarchy-aware (HRP) allocator; compare them walk-forward on net-of-cost equity curves, reporting weight turnover, calm-period Sharpe, and crash-window drawdown for each.
  4. Deploy the chosen scheme to the demo account through the supervisor config: magic-number segregation, margin budget with ceiling, weight bands, and per-sleeve ladder tiers wired in.
  5. Run at least eight weeks on demo with the full telemetry stack: book-level drawdown attribution, factor-exposure and leverage monitors, weekly TCA reviews feeding cost assumptions back into research.
  6. Write the operations runbook: startup/shutdown, restart recovery, kill-switch rehearsal record, reconciliation cadence, escalation paths, and the quarterly re-validation ritual.

Acceptance criteria:

  • Every sleeve passed its own validation ladder before joining the book (dates recorded).
  • Correlation report shows calm AND crash windows for every sleeve pair; overlaps resolved.
  • Allocation schemes compared walk-forward, net of costs, with turnover and crash metrics.
  • Chosen scheme’s simulated combined equity beats the fixed-capacity baseline on max drawdown at comparable return, or the memo explains honestly why it does not.
  • Demo book runs ≥ 8 weeks with journal-versus-broker reconciliation clean throughout.
  • Margin ceiling, bands, and per-sleeve ladder tiers verified active by fault injection.
  • Operations runbook complete, including a rehearsed kill-switch and the quarterly ritual checklist with owners and due dates.

Key Takeaways

  • Combining weakly-correlated mediocre edges beats concentrating on one strong edge on every survival metric — Sharpe, drawdown depth, recovery time, and capacity all improve together.
  • Correlations spike in crashes (+0.21 calm → +0.68 crash on Module 16’s cross-asset panel); size the book as if the crash correlation were the real one, because on the day that matters, it is.
  • Inverse-vol is the honest default upgrade: it converts unequal-capital sleeves into equal-risk contributors (48.4/32.3/19.4 on 8/12/20 vols → 3.87% risk each, ~6.7% book vol uncorrelated, ~10.3% at crash correlations).
  • Mean-variance is an error maximizer: it converts estimation noise into precise-looking poor allocations. Shrinkage, bands, and hierarchy (HRP) are all structured humility.
  • The evidence cuts both ways on HRP: out-of-sample, shrunk min-vol often achieves lower realized vol; HRP’s proven advantage is weight stability and robustness, not guaranteed lower volatility.
  • One MT5 account runs many strategies safely only with unique magic numbers, hedging-mode confirmation, and a summed-margin ceiling — netting accounts silently merge hedges.
  • Bands beat beauty: rebalance on threshold breaches and a quarterly calendar, budget the turnover like any other cost, and never execute rebalances in illiquid hours.
  • The book drifts even when no sleeve does: watch factor creep, hidden leverage, and the quarterly re-validation ritual as first-class controls, not paperwork.

References

  • Marcos López de Prado — Building Diversified Portfolios that Outperform Out-of-Sample (Journal of Portfolio Management, 2016) — the HRP paper: tree clustering + recursive bisection without covariance inversion
  • Wikipedia — Hierarchical Risk Parity (concise algorithm summary and links)
  • PortfoliosLab — MVO vs HRP: An Out-of-Sample Comparison (min-vol beat HRP on realized volatility in ~92% of quarterly-rebalanced portfolios; the honest counterpoint)
  • PyPortfolioOpt documentation — pyportfolioopt.readthedocs.io (HRPOpt, shrinkage covariance, working examples used above)
  • Riskfolio-Lib documentation — riskfolio-lib.readthedocs.io (risk-parity family, HRP variants, convex formulations beyond this module’s scope)
  • Richard Grinold & Ronald Kahn — Active Portfolio Management (McGraw-Hill) — the institutional treatment of combining signals into portfolios and the value-add calculus
  • Robert Carver — Systematic Trading (Harriman House, 2015) — top-down book-level risk budgeting philosophy that survives contact with retail infrastructure

You made it. This closes the sequence — Modules 0 through 25 are complete: from market microstructure through single-system design, validation engineering, and now the combination layer that turns validated parts into a survivable trading business. Continue to Appendices.