Monte Carlo · Methodology

Walk-Forward Re-optimization

A static simulation answers “what happens to these weights?” Walk-forward re-optimization answers a different question: “what happens if I keep running my process?” The optimizer is re-fitted independently on every path, on that path's own simulated history, and it pays the tax and trading costs of the trades it generates.

Two rebalance modes

rebalance_mode: static, the default

Every path is pushed back to the one weight vector the parent optimizer produced on real history. The weight rule is frozen, so the only thing the simulation varies is the market.

rebalance_mode: reoptimize

At each re-optimization date, every path re-fits the parent's optimizer on the real panel plus everything that path has simulated so far, then trades into the new vector. The result carries the estimation noise of doing that, which is the point: a strategy that looks good on one realized history may or may not survive being re-estimated on thousands of plausible ones.

Re-optimization requires the parent run to have recorded the optimizer specification behind its weights. A parent optimized before that was persisted can still be simulated with static, which replays its stored weights without re-fitting. There is no equal-weight, static or alternate-linkage fallback: a request the engine cannot honour exactly is refused at admission rather than silently downgraded.

Which optimizers can be re-fitted

Re-fitting inside the simulation means solving the allocator once per path per re-optimization date. At 50,000 paths and annual re-fits over 20 years that is a million solves, so only optimizers with a batched closed-form rule qualify. Convex solvers are not re-fittable at this scale and are refused rather than approximated.

MethodNotes
HRPHierarchical risk parity. Correlation-distance clustering plus recursive bisection, batched across paths.
HERCAdmitted only when the parent run attested its complete Riskfolio specification.
InverseVolatilityWeights proportional to the reciprocal of each holding's estimated volatility.
EquiWeightedThe 1/N control. Included because it isolates the cost of trading from the value of the weight rule.
QuintileMomentumA selection strategy. Requires the parent's ranked candidate universe, described below.

The batched HERC port reproduces SciPy's nearest-neighbour chain exactly, including previous-node preference on a tie, larger-slot survival, stable distance sorting and relabelling. The pinned four-asset equicorrelation case, the hardest exact-tie configuration, matches the reference implementation to .

Policy knobs

Every default reproduces the original behaviour exactly, so adding the walk_forward block to a request changes nothing until you set a field.

reoptimize_frequency

The re-optimization grid is separate from the rebalance grid. A portfolio can rebalance monthly while re-fitting annually, and that is the much cheaper policy, because rebalancing is linear work and re-fitting is quadratic. Between re-fits the portfolio still trades, but it trades back to the most recently fitted vector.

The interval must be a whole multiple of the rebalance interval. A fit that no rebalance can adopt is a fit that never happened, so the engine refuses the configuration rather than accepting a grid whose results would be indistinguishable from a coarser one.

estimation_window and estimation_window_years

expanding is the default: at each re-fit a path estimates on the entire real panel plus everything it has simulated. It matches the production rolling backtest's default window type, so simulation and backtest agree, and it needs only running moments, which makes its memory flat in the horizon.

rolling forgets. The fit sees a trailing window of fixed length and nothing before it, which is the more faithful description of a practitioner who re-estimates on recent data. It is not free: running moments cannot subtract, so the engine retains one moment block per re-optimization period inside the window. Admission prices that in bytes and refuses the run when it would not fit, rather than discovering it mid-simulation. The floor is two years, below which a daily covariance over up to 50 assets is not worth estimating.

weight_observation_grid

Cross-path weight and turnover trajectories are observed annually by default: plus every anniversary, whatever the rebalance frequency, so a 40-year monthly run reports 40 points rather than 480. Setting rebalance observes every rebalance instead, which is what a monthly-rebalanced run needs in order to show intra-year weight movement. The finer grid multiplies the artifact block by the number of rebalances per year, so admission caps the cell count.

Selection strategies and the candidate universe

For an allocator over a fixed universe, the assets a parent held and the assets it chose between are the same set. For a selection strategy they are not, and treating them as the same silently changes the strategy.

QuintileMomentum holds one quintile. Its unheld candidates are absent from the stored weight vector by construction, so a re-fit restricted to the held set would re-select the top quintile of the quintile. On 20 candidates the parent holds 4 and a naive re-fit holds 1, while still calling itself QuintileMomentum.

Such a parent therefore persists its ranked candidate list as a separate immutable input, next to but distinct from its weights. A re-optimizing run simulates that full universe with the unheld names at exactly zero on day zero. The weight-dust and fully-invested rules are untouched, so candidate membership is not smuggled in through them. A static run keeps simulating the held set alone, because the unheld names sit at zero for the whole horizon and widening the set would be bit-identical at roughly five times the workload.

Every run reports the candidate list, the simulated set, the held set and all three counts, so the distinction is auditable rather than implicit.

The batched rank allocator reads a price index rather than a covariance matrix. Rebalance dates are known before the path loop starts, so the engine snapshots the per-path price index only on the days a future re-optimization will ask for it. A re-optimization whose 126-row lookback reaches back before the simulation start splices the historical leg in exactly, as one precomputed factor per asset, rather than shortening the window and quietly making early re-optimizations a different strategy from late ones.

The paired frozen-weight control arm

Setting paired_static_baseline simulates the frozen-weight arm on the same seed and the same random-stream layout. Path in one arm is path in the other: identical shock coordinates, different weight rule. Under those common random numbers, the difference between the arms is attributable to the weight rule alone rather than to two independent draws that happen to differ.

This doubles worker time, which is why it is opt-in and is charged to the workload envelope. Every paired chart carries the same footnote: the two arms ran identical paths, and a ratio above 1 is not evidence that re-optimizing outperforms. Establishing that would require out-of-sample validation, which is covered on the rolling-origin validation page.

Cost attribution: did re-optimizing pay for itself?

A cost_attribution block splits every rupee of friction by the event that caused it, across initial investment, contribution, withdrawal, rebalance and terminal tax and slippage. It reports two units: rupees paid over the horizon, and each payment compounded at the path's own realized growth from the moment it was paid.

When the paired control arm ran, the two arms differ only in the weight rule, so the frictions subtract pathwise and the residual is the allocation effect. That closes the identity

which is precisely the question a re-optimization policy has to answer. It is a first-order attribution: it holds each path's realized returns fixed, so it does not model the second-order effect of a payment on the returns that followed it.

Workload and admission

Clustering allocators are charged quadratically in assets, because a correlation-distance clustering step is . The re-optimization envelope prices

against a fixed budget before a worker is occupied. A rank allocator such as QuintileMomentum declares that it reads a price index rather than a covariance matrix, so it is charged linearly in assets instead. Declaring the state is what fixes the pricing.

Alongside that, every run reports a path-aware peak-memory estimate in execution_limits.estimated_peak_memory_bytes. Admission and the worker apply the same workload and optimizer-attestation checks, so a row queued under an older policy cannot bypass a newer safety gate when it finally executes.

Request example

json
{
  "method": "HRP",
  "horizon_years": 20,
  "path_count": 20000,
  "rebalance_frequency": "monthly",
  "rebalance_mode": "reoptimize",
  "paired_static_baseline": true,
  "walk_forward": {
    "reoptimize_frequency": "annual",
    "estimation_window": "rolling",
    "estimation_window_years": 10,
    "weight_observation_grid": "rebalance"
  },
  "slippage_bps": 15
}

This rebalances monthly, re-fits annually on a trailing ten-year window, records weights at every rebalance, and runs the frozen-weight control arm on the same shocks. See the Monte Carlo API reference for the complete request schema.

What this does not establish

A walk-forward simulation is conditional on the selected return generator, the frozen optimizer-time inputs, the effective configuration, the engine version and the seed. It is not a calibrated forecast.

No rebalancing policy on this platform has been shown to outperform another out of sample. The paired arm makes the comparison clean, in the sense that it removes sampling noise from the contrast. It does not make the comparison predictive.

References

  • Glasserman, P. (2004). Monte Carlo Methods in Financial Engineering. Springer. Chapter 4 covers common random numbers as a variance-reduction technique for paired comparisons.
  • L'Ecuyer, P. (1994). Efficiency Improvement and Variance Reduction. Proceedings of the Winter Simulation Conference.
  • López de Prado, M. (2016). Building Diversified Portfolios that Outperform Out of Sample. Journal of Portfolio Management, 42(4), 59–69. The hierarchical risk parity construction that the batched re-fit reproduces.
  • Bailey, D. H., Borwein, J., López de Prado, M., & Zhu, Q. J. (2014). Pseudo-Mathematics and Financial Charlatanism. Notices of the AMS, 61(5), 458–471. On why an in-sample comparison of two weight rules is not evidence.

Not investment advice. Past performance is not indicative of future results.