Project 02 / Portfolio construction & risk
Strata
A portfolio construction and risk system: build an allocation from any set of tickers, or let an optimizer solve for one, then see what the rule earned out of sample, what it cost to run, and the range of outcomes that could follow.
- Test window
- Jan 2019 – Aug 2026
1,923 daily sessions, CAD - Universe
- 8 TSX stocks · 5 sectors
XIC.TO benchmark - Rules
- Growth · Income · Balanced
Low volatility · 12 optimizers - Stack
- Python · pandas · NumPy
SQLite · cvxpy · FastAPI
CAD 100,000 per portfolio from Jan 2019 to Aug 2026, rebalanced monthly to target, 10 bps on traded notional. Optimizers re-solve at each rebalance from the previous 252 sessions and are judged only on the returns that followed. Benchmark: XIC.TO, held without rebalancing.
The thirty-second version
Balance beat concentration on risk-adjusted terms
Balanced returned 19.1% a year against XIC's 16.2% at almost the same volatility, and its worst loss was −28.7% against the index's −37.2%. One sample and one universe, so this is an observed result, not evidence of persistent skill.
Growth's return came with half its value at risk
A 65% technology target compounded at 27.8% a year but fell −48.7% from peak. In five-year scenarios it drops below 80% of the starting balance in 24.2% of paths, against 8.8% for Balanced, and finishes behind Balanced in 19.3% of shared market paths.
Optimizing did not beat equal weight
Minimum variance achieved lower volatility (15.2% against 16.9%) but a Sharpe of 0.79 against 1.00 for equal weighting, with 2.7× the turnover. An optimal answer to estimated inputs is not the best realized portfolio; the system reports that instead of assuming the sophisticated method wins.
The application
Build a portfolio, test it, see where it could land
Strata is the deployed version of the research: enter Yahoo Finance tickers and weights, or pick one of twelve registered construction methods, and a single run returns the realized history, the costs, a drawdown profile and 5,000 simulated five-year paths. Up to four portfolios run side by side in the same simulated markets, so differences come from construction rather than a luckier draw.



Portfolios & strategy
Tickers and weights, or a registered method: minimum variance, risk parity, maximum diversification, maximum Sharpe and their convex variants.
Constraints
Position caps, sector and country limits, turnover budgets, tracking-error ceilings, and optional spread and impact costs.
Risk models
Sample covariance, Ledoit-Wolf shrinkage, or a statistical factor model whose factor count follows the Marchenko-Pastur noise edge.
History & drawdowns
Monthly rebalancing with daily drift, costs on traded notional, rolling one- and three-year return ranges, depth of every drawdown.
Five-year scenarios
5,000 block-bootstrap paths shared across portfolios, head-to-head comparisons in the same markets, and sampling intervals on each percentile.
Run records
Every result carries an identifier built from the request, the price data and the source code, with library versions, and exports in full.
Historical results
What CAD 100,000 in each policy became
Growth, Income and Balanced hold fixed target weights; Low volatility weights by inverse volatility estimated from the first 252 sessions and then frozen. Weights drift daily and reset at the first close of each month, and costs come out of the portfolio before the next day's return. Log scale, so equal slopes mean equal percentage growth.
Drawdown from the running peak
| Portfolio | Total return | Annual return | Volatility | Sharpe | Max drawdown | Costs paid |
|---|---|---|---|---|---|---|
| Growth | 549% | 27.8% | 28.6% | 0.90 | −48.7% | $1,857 |
| Balanced | 280% | 19.1% | 16.3% | 0.97 | −28.7% | $777 |
| Income | 213% | 16.1% | 16.1% | 0.83 | −33.1% | $396 |
| Low volatility | 244% | 17.6% | 15.3% | 0.94 | −29.7% | $574 |
| XIC benchmark | 215% | 16.2% | 16.4% | 0.82 | −37.2% | $0 |
Did optimizing help?
Optimized weights, judged only on what came next
Beyond fixed targets, Strata re-solves the weights at every monthly rebalance from the previous 252 sessions, using Ledoit-Wolf covariance and a 35% cap per holding, and holds them over returns the optimizer never saw. Equal weighting on the same dates is the honest control: it needs no estimates at all.
| Construction rule | Annual return | Volatility | Sharpe | Turnover a year | Costs paid |
|---|---|---|---|---|---|
| Minimum variance | 14.9% | 15.2% | 0.79 | 1.40 | $1,698 |
| Risk parity | 18.1% | 15.2% | 0.98 | 0.55 | $826 |
| Equal weight | 20.2% | 16.9% | 1.00 | 0.51 | $886 |
| XIC benchmark | 16.2% | 16.4% | 0.82 | 0.00 | $0 |
Five-year scenarios
The same CAD 100,000, five more years
Every portfolio starts a fresh CAD 100,000 on Aug 2026 and is compounded through the same 5,000 sequences of 20-session blocks drawn from its completed history. Shared blocks mean Growth and Balanced face identical markets in each path, so they can be compared path by path. Shaded: 5th–95th and 25th–75th percentiles; line: median.
Growth
Balanced
| Portfolio | 5th percentile | Median | 95th percentile | Ends below start | Below 80% at any point | Median worst drop |
|---|---|---|---|---|---|---|
| Growth | $114,708 | $329,002 | $872,362 | 3.1% | 24.2% | −33.5% |
| Balanced | $131,665 | $235,938 | $394,953 | 0.7% | 8.8% | −20.3% |
| Income | $119,698 | $211,349 | $341,758 | 1.6% | 12.2% | −22.1% |
| Low volatility | $129,046 | $222,660 | $355,250 | 0.8% | 9.2% | −19.4% |
| XIC benchmark | $106,182 | $213,678 | $360,350 | 3.9% | 16.1% | −24.2% |
How it works
A traceable path from prices to a published data mart
Checksummed inputs
Adjusted prices from yfinance or a frozen CSV verified by SHA-256, target holdings and a security master.
Reject bad data
Conflicting duplicates, missing or non-finite prices, unknown holdings and incomplete ticker-date coverage all stop the run.
Portfolio accounting
Previous-close weights, daily drift, monthly resets, costs on traded notional, then returns, risk and drawdowns.
SQL data mart
Five tables replace the SQLite snapshot atomically after positions reconcile to NAV; failed runs keep the previous database.
Accounting conventions
Portfolio return
rp,t = ∑ wi,t−1 ri,tWeights from the previous close. End-of-day weights would let today's move choose the allocation that earned it.
Trading cost
cost = V × ∑ |wtarget − wdrifted| × 0.00110 bps on total traded notional, purchases and sales, charged before the next return.
Annualized return
CAGR = (Vend / Vstart)252/N − 1On net portfolio value over N observed daily returns.
Sharpe ratio
mean(r − rf) / s(r) × √252A fixed 3% annual risk-free rate, converted to a daily compound rate.
Drawdown
Dt = Vt / max(V0…Vt) − 1Starting capital counts as the first peak, so an immediate loss registers.
Scenarios
5,000 paths × 1,260 sessionsBlocks of 20 completed net returns, the same block draws for every portfolio; costs are embedded in the history sampled.
The data mart holds securities, security_daily_analytics, portfolio_positions, portfolio_daily_summary and pipeline_runs. The analysis queries use CTEs, ROW_NUMBER for month-end NAV, LAG for compounded returns and DENSE_RANK for volatility ranking.
Research to production
A contract between a researcher's rule and running software
Each construction method is a function from a context to a weight vector, registered with metadata. The API advertises the registry, the interface builds its menu from it, and the walk-forward loop calls the same contract, so a new method inherits every mandate limit and check without knowing they exist.
Conformance gate
Fifteen checks on every method
Valid weights, respected constraints, repeatable results, asset-order invariance, degenerate inputs and a solve-time budget. It runs in CI and fails the build; tests feed it deliberately broken methods to prove it rejects them.
12 of 12 registered methods passWhat the gate caught
Defects with investment consequences
A maximum-Sharpe solver that blew its 750 ms budget with redundant restarts, mandate parameters leaking between objectives, and a ratio transform whose answer changed with the units of the returns.
Each now covered by a regression testProvenance
Every result has a run record
An identifier built from the request, the price data and the source code, with library versions. Two results are the same only if their records match, not because their links do.
Checksummed inputs · run manifestOperations
Honest failure modes
Runs report progress, reuse cached results that match exactly, and surface data-source failures rather than silently substituting frozen prices. Solver status and constraint duals are reported, so infeasible is never shown as optimal.
Deployed on Render · offline single-file demoValidation
Tests aimed at the errors that would change the conclusion
- Each day's return is earned by the weights held at the previous close
- Low-volatility weights come from the first 252 sessions only; rewriting later prices leaves them unchanged
- Rewriting the final month of prices leaves every earlier optimized weight unchanged
- Scenarios start after the last completed backtest date and sample only completed returns
- Positions reconcile to NAV before the data mart is published
- Paired scenario differences are tested against subtracting separate percentiles
- Three independent implementations of two optimizers agree, including a MATLAB cross-check under Octave
- A 10% loss then a 10% gain compounds to a 1% loss; costs reduce final value
Next research iteration
Test the downside harder before trusting the upside
Rerun the scenarios under explicit adverse regimes and other historical windows, add a trading calendar and coverage audit, scale the market-impact model by average daily volume, replace the cardinality heuristic with a mixed-integer program, and use point-in-time universes once membership and delisting data are available. If an allocation only looks attractive under one set of assumptions, that dependence belongs in the decision.
Interactive report · Executed notebook · Performance CSV · Construction CSV · Scenario downside CSV · Optimization methods
Portfolio construction
Fixed-target and inverse-volatility policies, walk-forward convex optimization with mandates, shrinkage and factor risk models, cost-aware rebalancing.
Risk & simulation
Drawdown analysis, block-bootstrap scenarios shared across portfolios, paired comparisons, sampling sensitivity and percentile intervals.
Data engineering
Validated ETL with checksums, an atomically published SQLite mart, reconciliation, run provenance, a strategy contract with a CI conformance gate.
Try it
Build a portfolio and see where five years could land
The free instance may take a minute to wake up; the browser demo runs instantly on nine frozen securities. Scenarios are simulations, not predictions, and nothing here is investment advice.