Use your browser's “Save as PDF” destination for a paper-ready file.
QuanterLab
QuanterLab Research · Pre-registered Walk-Forward Study

Regime aware portfolio

A pre-registered, point-in-time walk-forward investigation with a multiple-testing (deflated Sharpe) correction.
Universe · S&P 500 (point-in-time constituents)
Method · Evolving (fork-and-prune per step)
Step size · 1 year per forward window
In-sample · 2 years before each anchor
Out-of-sample span · 2016-01-04 → 2023-12-29
Compiled · July 23, 2026
Abstract

We pre-register and walk a strategy forward across S&P 500 in 1-year steps, each tested out-of-sample on data the strategy had never touched, with survivorship-bias-free constituents reconstructed as of every anchor. Stitched end to end, the walk realises an annualised Sharpe of 0.45 over 2005 out-of-sample observations. Deflating for the 11 trials the search actually ran gives a probabilistic deflated Sharpe of 36% — the strategy does not survive the multiple-testing correction — most likely an artifact of the search. Every attempt made along the way — 11 registered circuits, pruned candidates included — is counted in the correction, so the headline number reflects the true cost of the search.

1  Methodology

The study is sealed before it starts: the universe, the step size and the method are fixed at registration and cannot be changed while the walk is running, and the anchor advances only forward. This removes the two commonest ways a backtest flatters itself — moving the test window until the numbers look good, and changing the rules with hindsight.

At each step the strategy is fitted on the 2 years of history ending at the anchor and then tested on the subsequent out-of-sample window it had never seen. Index membership is reconstructed as of the anchor date from the exchange's dated constituent change-log, so names that were later removed or delisted still compete on the dates they actually traded and there is no survivorship bias. Under the evolving method, each step may try several candidate circuits; one is kept and the rest are pruned. Crucially, every candidate — kept or discarded — is registered and counts toward the trial total below.

The inferential statistic is the Deflated Sharpe Ratio (Bailey and López de Prado, 2014), which asks: given that 11 configurations were tried, what is the probability that the observed Sharpe is genuinely positive rather than the best of many noisy draws? The number of trials is frozen at compile time and taken as the larger of the registered circuit count and the number of logged runs, so it can never understate the search.

2  Results

2.1  Headline

Pooled deflated Sharpe
36%
annualised SR 0.45 · 2005 OOS bars
Trials in the search (N)
11
11 registered
Stitched total return
+64.5%
benchmark +133.4%
0.78x1.62x2.46x
Figure 1. The whole walk, stitched. Each step's out-of-sample leg is chained in calendar order into one continuous growth-of-1 track (green), with the benchmark (grey) compounded through the identical windows. Dotted verticals mark the step boundaries; the dashed horizontal is break-even. The translucent bands are the circuit's own detected regime —  calm,  choppy,  stressed (Table 2).

2.2  Per-step results

Table 1. One row per surviving step. The deflated Sharpe uses the cumulative trial count at that step, so it falls as the search widens — the decay is the point.
#StepOut-of-sample window NSharpeDeflated
1 Regime aware portfolio · step 1 2016-01-04 → 2016-12-30 1 0.32 62%
2 Regime aware portfolio · step 2 2017-01-03 → 2017-12-29 4 1.89 77%
3 Regime aware portfolio · step 3 2018-01-02 → 2018-12-31 5 -0.91 2%
4 Regime aware portfolio · step 4 2019-01-02 → 2019-12-31 6 0.25 15%
5 Regime aware portfolio · step 5 2020-01-02 → 2020-12-31 7 0.08 10%
6 Regime aware portfolio · step 6 2021-01-04 → 2021-12-31 8 1.21 40%
7 Regime aware portfolio · step 7 2022-01-03 → 2022-12-30 9 -0.06 6%
8 Regime aware portfolio · step 8 2023-01-03 → 2023-12-29 10 1.03 29%
0.75x1.08x1.41x
Figure 2. Every step's out-of-sample curve overlaid, each rebased to 1× at its own start. Read alongside Table 1: consistent shape across steps is the walk-forward's evidence; a single lucky leg is not.

2.3  Trial accounting

The search registered 11 circuits in total (pruned candidates included); the correction uses this registered-circuit count, N = 11. The pooled deflated Sharpe is the inferential headline; the per-step deflated track in Table 1 is illustrative, since near-identical variants are correlated and per-step deflation over-penalises.

2.4  Regime routing and capital exposure

The circuit routes capital by market regime: a classifier labels each period calm, choppy or stressed and the strategy re-weights — and, crucially, de-risks — accordingly. Those states are the translucent bands behind Figure 1. This is where a regime overlay earns or loses its keep: the table below shows how often the book sat in each state and how much capital it actually put to work there versus held idle in cash. A model that retreats to cash in choppy and stressed periods gives up return for a smoother ride — the trade-off is visible here, not hidden in the headline.

Table 2. Time spent in each detected regime and the average capital deployed there. The remainder is idle cash — the drag when the router de-risks.
RegimeTime in stateCapital deployedIdle cash
Calm 54.5% 100.0% 0.0%
Choppy 21.7% 75.0% 25.0%
Stressed 23.8% 50.0% 50.0%

3  Strategy specification and evolution

Each step's strategy is a circuit of platform primitives, frozen at registration. The table lists the full component set per step and — where the hypothesis evolved — exactly what changed against the previous step: components added, removed, or re-tuned.

Table 3. The frozen component specification per step. Parameters shown are the reproduction-relevant ones; the complete circuit is preserved in the study ledger (Appendix A).
#Components (primitives & key parameters)Evolution vs previous step
1 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y)
2 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
3 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
4 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
5 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
6 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
7 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward
8 universe (preset=sp500) · price loader · factor loader · factor value · factor quality · factor momentum · factor growth · factor regime tilt · factor composite · regime gmm (n_regimes=auto) · vix regime · hmm vol · regime ensemble (method=majority) · factor top tier · portfolio backtest (horizon=1y, rebalance=quarterly, selection=reselect) · portfolio forward autopsy · monte carlo (horizon=1y) unchanged — carried forward

4  Pre-registration record

The integrity of a walk-forward rests on registering each hypothesis before its out-of-sample window is scored. The order below is the order in which the hypotheses were sealed.

Table 4. The registered hypothesis for each step, with its registration timestamp.
#Registered hypothesisAnchorRegistered at
1 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2016-01-01 2026-07-22
2 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2017-01-01 2026-07-22
3 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2018-01-01 2026-07-22
4 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2019-01-01 2026-07-22
5 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2020-01-01 2026-07-22
6 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2021-01-01 2026-07-22
7 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2022-01-01 2026-07-22
8 “S&P 500, rebalanced quarterly across the selected basket, conditioned on the wired regime classifiers, and run out-of-sample from the anchor — with no in-sample parameters to optimize, a walk-forward does not apply; its disposition is the realized forward path versus the benchmark, is expected to generate positive risk-adjusted returns over the forward test window.” 2023-01-01 2026-07-22

5  Discussion

A garden of forking paths (Gelman and Loken, 2013) is not cheating — it is what honest research feels like from the inside. At each step there were several defensible things to try, and trying them is the work. What separates research from a hunt for a flattering backtest is that the paths not taken are still counted. Here they are: an annualised Sharpe of 0.45 over the stitched walk, deflated to 36% once the 11 trials are accounted for. The strategy does not survive the multiple-testing correction — most likely an artifact of the search.

Pooled deflated Sharpe is the inferential number; the per-step DSR track is illustrative (near-identical variants are correlated, so per-step N over-deflates). N is a conservative upper bound on the multiple-testing penalty, frozen at compile time and taken as the larger of the lineage trial count and the explored-runs count so it cannot understate the search. Integrity rests on pre-registration: each step's submitted_at is the registration order, auditable against when its OOS was scored.

References

  1. Bailey, D. H., & López de Prado, M. (2014). The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality. Journal of Portfolio Management, 40(5), 94–107.
  2. Gelman, A., & Loken, E. (2013). The garden of forking paths: Why multiple comparisons can be a problem, even when there is no “fishing expedition.” Working paper, Columbia University.
  3. Harvey, C. R., Liu, Y., & Zhu, H. (2016). … and the Cross-Section of Expected Returns. Review of Financial Studies, 29(1), 5–68.
  4. Lo, A. W. (2002). The Statistics of Sharpe Ratios. Financial Analysts Journal, 58(4), 36–52.

Appendix A  Reproducibility

Each step is backed by a frozen run report. The study is re-derivable from the ledger below.

#CommitReportAnchorOOS window
1 f5d34a004116 256 2016-01-01 2016-01-04 → 2016-12-30
2 007b403b1f75 257 2017-01-01 2017-01-03 → 2017-12-29
3 3e4d52220a7b 258 2018-01-01 2018-01-02 → 2018-12-31
4 2c97fa4cd586 259 2019-01-01 2019-01-02 → 2019-12-31
5 0d446a9e665c 260 2020-01-01 2020-01-02 → 2020-12-31
6 b544e634397b 262 2021-01-01 2021-01-04 → 2021-12-31
7 bf99842d97c5 264 2022-01-01 2022-01-03 → 2022-12-30
8 9de35299dcf6 265 2023-01-01 2023-01-03 → 2023-12-29

Appendix B  Per-step diagnostics

What each step's run actually did beyond its return: capital allocation across lanes and regimes, the portfolio book's rebalancing and cost drag, and how positions were sized. Harvested from the frozen run reports — present where the circuit produced them.

Step 1 · 2016-01-04 → 2016-12-30

Portfolio book — rebalanced quarterly · 4 constructions · 18 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 2 · 2017-01-03 → 2017-12-29

Portfolio book — rebalanced quarterly · 5 constructions · 16 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 3 · 2018-01-02 → 2018-12-31

Portfolio book — rebalanced quarterly · 5 constructions · 14 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 4 · 2019-01-02 → 2019-12-31

Portfolio book — rebalanced quarterly · 5 constructions · 11 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 5 · 2020-01-02 → 2020-12-31

Portfolio book — rebalanced quarterly · 4 constructions · 19 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 6 · 2021-01-04 → 2021-12-31

Portfolio book — rebalanced quarterly · 5 constructions · 19 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 7 · 2022-01-03 → 2022-12-30

Portfolio book — rebalanced quarterly · 5 constructions · 17 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Step 8 · 2023-01-03 → 2023-12-29

Portfolio book — rebalanced quarterly · 5 constructions · 19 names held · selection: reselect · 0% in cash · turnover 0× · cost drag 0% · regime gate active

Appendix C  The mathematics of the circuit

Every primitive this study wired, with the mathematics it actually computes — the same formulas the execution engine runs, as documented in the platform's per-primitive "Explain the math". Nothing here is illustrative; it is the calculation.

C.1  Universe

The starting set of tickers — resolved point-in-time so there is no survivorship bias.

Before any math, you need a list of stocks. An index preset (S&P 500, Nasdaq-100, Dow 30) is reconstructed as it stood ON your anchor date by replaying the historical add/drop change-log backwards — so a 2018 backtest sees the 2018 membership, not today's winners.

Point-in-time membership

Start from today's constituents and un-apply every membership change after the anchor t:

\mathcal{U}(t) = \mathcal{U}_{\text{now}} \;\ominus\; \{\text{adds after } t\} \;\oplus\; \{\text{drops after } t\}
Constituents resolved from the index change-log; the same point-in-time set the factor + screening modules use.

C.2  Price Loader

Bulk OHLCV fetch for the whole universe — point-in-time, no future bars.

Momentum, volatility, trend — every price-based metric needs history. This loads open/high/low/close/volume for all names in parallel, clipped so nothing after the anchor can leak in. The lookback window is derived automatically from the deepest metric you wired.

The window is derived, not guessed

It loads exactly enough history for the hungriest downstream metric plus a warm-up buffer:

W = \max_k(\text{lookback}_k) + \text{buffer}, \qquad \text{bars} \le \text{anchor } t

C.3  Factor Loader

Point-in-time fundamentals — never let the user see a number before the SEC did.

Loads ~23 fundamental metrics (valuation, quality, growth) per name, but with one inviolable rule: a financial statement becomes visible only on or after its SEC acceptedDate. A 2020 backtest sees only what was actually filed by 2020 — no look-ahead, ever.

The PIT gate
\text{visible}(f, t) \iff \text{acceptedDate}(f) \le t
Missing acceptance dates fall back to filingDate, else statement date + 45 days.
Byte-identical to FM101FBKT (shared_libs/factor_core). US indexes only (SEC reliability).

C.4  Factor Value

Value — how cheap is the stock, cross-sectionally?

Blends cheapness metrics — P/E, P/B, P/S, EV/EBITDA (lower is better) plus free-cash-flow and earnings yields (higher is better). Each metric is z-scored across the universe, winsorized against outliers, weighted by the importance you set (Off/Low/Med/High), and averaged.

Cross-sectional z-score per metric
z_{i,k} = \pm\,\frac{x_{i,k} - \bar x_k}{s_k}
Sign encodes direction (cheap = good). Winsorized at 1% / 99%.
Importance-weighted family score
\text{Value}_i = \frac{\sum_k \omega_k\,z_{i,k}}{\sum_k \omega_k}, \quad \omega \in \{0,\,0.5,\,1,\,2\}
Off / Low / Medium / High = 0 / 0.5 / 1 / 2. Binding rank happens in Factor Composite.
Byte-identical to FM101FBKT (shared_libs/factor_core).

C.5  Factor Quality

Quality — is this a strong, profitable, well-financed business?

Blends profitability (ROE, ROA, ROIC, margins) and balance-sheet strength (debt-to-equity inverted, current ratio, interest coverage). Same z-score, winsorize, importance-weight recipe as every factor family.

Family score
\text{Quality}_i = \frac{\sum_k \omega_k\,z_{i,k}}{\sum_k \omega_k}
Debt metrics enter inverted (less leverage = higher quality).
Byte-identical to FM101FBKT (shared_libs/factor_core).

C.6  Factor Momentum

Momentum — recent price strength, no filing required.

Pure price-based: 1/3/6/12-month returns measured at the anchor. Because it needs no financial statement, it is always available — even for names with thin fundamentals.

Multi-horizon return, z-scored
\text{Mom}_i = \frac{\sum_k \omega_k\, z\!\big(R_{i}^{(k)}\big)}{\sum_k \omega_k}, \quad R^{(k)} = \frac{P_t}{P_{t-k}}-1
Horizons k ∈ {1, 3, 6, 12} months, measured point-in-time at t.
Byte-identical to FM101FBKT (shared_libs/factor_core).

C.7  Factor Growth

Growth — is the business getting bigger, by the numbers it has filed?

Revenue growth, EPS growth, free-cash-flow growth and book-value-per-share growth, taken from the latest SEC-accepted filings (so growth is honest as-of the anchor). Same family recipe.

Family score
\text{Growth}_i = \frac{\sum_k \omega_k\,z_{i,k}}{\sum_k \omega_k}
Growth rates from PIT-visible filings only.
Byte-identical to FM101FBKT (shared_libs/factor_core).

C.8  Factor Regime Tilt

The radar — read the market and suggest which factors to lean on.

Four market signals vote on a recommended Value/Quality/Momentum/Growth weighting: the trend regime (momentum & growth in bull markets, quality & value in bear), the valuation spread, factor momentum, and cross-sectional dispersion. Their importances normalize to 100 and produce a tilt the Composite applies.

Signal-importance blend
\mathbf{w} = 0.30\,\mathbf{R} + 0.25\,\mathbf{V} + 0.25\,\mathbf{M} + 0.20\,\mathbf{D}
R = regime (5-vote benchmark-trend ensemble), V = valuation spread, M = factor momentum, D = dispersion. Importances renormalize to 100.
Optional wired classifier
\text{calm}\to\text{bullish},\ \ \text{choppy}\to\text{sideways},\ \ \text{stressed}\to\text{bearish}
Wire any Regime Classifier to replace the built-in trend ensemble.
Byte-identical to FM099 signal-guided engine (shared_libs/regime_core/factor_signals).

C.9  Factor Composite

The weighting console — blend Value, Quality, Momentum, Growth into one 0–100 score.

Where the four factor families become a single ranking. Each family score is standardized across the universe, blended with your slider weights (or the radar's suggested tilt), and min-max scaled to 0–100. Winsorizing tames outliers; z-score or percentile normalization is your choice.

Cross-sectional standardize + winsorize
z_{i,f} = \frac{x_{i,f} - \bar x_f}{s_f}\quad(\text{clipped at the 1st / 99th percentile})
Weighted blend, scaled to 0–100
C_i = \sum_f W_f\,z_{i,f}, \qquad \text{score}_i = 100\cdot\frac{C_i - \min_j C_j}{\max_j C_j - \min_j C_j}
W = your four slider weights (total 100) OR the Regime Tilt radar's suggestion. Needs ≥ 10 names, ≥ 3 valid metrics each.
Byte-identical to FM101FBKT ranking (shared_libs/factor_core.rank_stocks_at_date).

C.10  Regime Gmm

Gaussian mixture — cluster days into regimes, count chosen by BIC.

Treats each day as a point in (return, volatility) space and fits a Gaussian mixture; the Bayesian Information Criterion picks how many regimes the data actually support. States are vol-sorted and short runs de-noised. The rigorous detector the Per-Regime and Regression optimizers were designed around.

Mixture density + model selection
p(\mathbf x) = \sum_{k=1}^{K}\pi_k\,\mathcal N(\mathbf x\mid\boldsymbol\mu_k,\boldsymbol\Sigma_k), \qquad K^* = \arg\min_K \text{BIC}(K)
Features x = (log-return, realized-vol). K ∈ {2,3,4} or auto.

C.11  Vix Regime

VIX thresholds — the simplest fear gauge.

Reads the VIX (or a wired series) and classifies by two cut-offs: low VIX is calm, high VIX is stressed, in between is choppy. Confidence scales with how far past the boundary you are.

Threshold rule
\text{regime} = \begin{cases}\text{calm} & \text{VIX} < c\\[2pt]\text{stressed} & \text{VIX} \ge s\\[2pt]\text{choppy} & \text{otherwise}\end{cases}
Defaults c = 16, s = 25.

C.12  Hmm Vol

Hidden Markov model on volatility — regimes defined by turbulence.

Same HMM machinery, but the observed features are realized volatilities (short, long) and the VIX term-structure ratio. States are sorted by volatility: lowest = calm, highest = stressed.

Vol-feature emissions
\mathbf v_t = \big(\sigma_t^{\text{short}},\,\sigma_t^{\text{long}},\,\tfrac{\text{VIX}}{\text{VIX3M}}\big), \quad \mathbf v_t\mid(z_t=k)\sim\mathcal N(\boldsymbol\mu_k,\boldsymbol\Sigma_k)
States vol-sorted: lowest σ = calm.
Real hmmlearn fit; graceful stub if absent.

C.13  Regime Ensemble

Combine several classifiers into one honest verdict.

Blends multiple regime classifiers into a single per-bar label by majority (equal vote) or explicit weights. Equal votes stay equal — a 3-way tie resolves to the middle severity, never to whichever was wired first.

Weighted severity vote
\text{regime}_t = \operatorname{round}\!\Big(\frac{\sum_m \omega_m\,\text{sev}(c_{m,t})}{\sum_m \omega_m}\Big)
sev maps calm/choppy/stressed → 0/1/2.

C.14  Factor Top Tier

The cut out of the factor lane — keep the top-ranked names.

Takes the composite-ranked factor set and keeps the best N, carrying the composite score, the four family scores and the point-in-time market cap for each survivor. Feed 10–20 to a direct portfolio, or 30–100 as an optimizer pool.

Rank cut
\{\, i : \operatorname{rank}(C_i) \le N\,\}, \quad C_i = \text{composite score}
Byte-identical to FM101FBKT ranking (shared_libs/factor_core).

C.15  Portfolio Backtest

Replay the portfolio forward — rebalanced, point-in-time, with costs.

Holds the basket and rebalances on schedule, re-selecting and re-optimizing point-in-time at each rebalance (so it only ever uses information available then), and reports the equity curve, Sharpe, drawdown and trade stats — optionally net of cost and risk overlays.

Compounded equity
E_t = E_{t-1}\big(1 + \mathbf w_{t}^{\top}\mathbf r_t - \text{costs}_t\big)
Drawdown
\text{DD}_t = \frac{E_t}{\max_{\tau\le t}E_\tau} - 1, \qquad \text{MaxDD} = \min_t \text{DD}_t

C.16  Monte Carlo

Monte Carlo — simulate thousands of futures for the portfolio.

Estimate the portfolio's drift and covariance, then roll it forward thousands of times under geometric Brownian motion. The spread of outcomes gives the probability of doubling, of a big loss, and the P5–P95 cone — the honest range around any single backtested path.

Geometric Brownian motion step
S_{t+\Delta t} = S_t\,\exp\!\Big[\big(\mu - \tfrac12\sigma^2\big)\Delta t + \sigma\sqrt{\Delta t}\;Z\Big],\quad Z\sim\mathcal N(0,1)
Seeded → reproducible. 1,000 / 5,000 / 10,000 paths over a 1–3y horizon.
Outcome probabilities
P(\text{double}),\ P(\text{+50\%}),\ P(\text{loss}),\ P(\text{−25\%}) = \frac{\#\{\text{paths in region}\}}{\#\text{paths}}
Byte-identical to FM095MCSX (shared_libs/portfolio_risk).
QuanterLab · Study be39e7ebad9b · compiled July 23, 2026. Point-in-time constituents, transaction costs and pre-registration timestamps are enforced by the platform. This report is generated from the frozen study artifact and is reproducible from the ledger above.
Run a study like this yourself

This paper was produced end-to-end in QuanterLab: the pre-registration, the sealed walk, the point-in-time data, the statistics and the document you are reading. The platform is in closed beta.

Request access More research RSS