Equal weight or inverse volatility for a fixed stock basket?
Comparing equal weight with inverse volatility requires more than normalizing two weight vectors. Use the same fixed stock basket, information window, rebalance schedule and eligibility policy, then execute trades with cash and costs and let weights drift between rebalances. A risk tool’s normalized output is a target, not a complete funded account.
This guide runs both schemes on three invented stock return histories. It is a long-only, single-currency research extension with fractional shares and simplified execution. CoTrading functionality remains planned or unverified. The numerical results are synthetic, not historical performance or a recommendation to use either allocation rule.
Freeze the basket and common eligibility
Choose the securities before comparing methods and record their stable identities and membership period. This is a weighting question for a fixed basket, not a factor-selection exercise. Keep data, calendar, costs, capital and terminal marking common so a different universe cannot explain the apparent advantage.
Both methods use the same three completed prior returns at every rebalance. Exclude a stock from both when history is short, missing or nonfinite, or sample volatility is at most 1e-12. The threshold is an explicit fixture policy, not a universal market standard. A zero-volatility asset is not allowed to receive infinite inverse weight.
This shared policy means equal weight is applied to the same eligible subset as inverse volatility. That differs from an alternative study in which equal weight retains a constant-price stock while the other rule drops it. Either question can be researched, but changing eligibility silently confounds the comparison.
Calculate targets using known history only
Equal weight assigns 1/m to each eligible stock. Inverse volatility assigns (1/sigma_i) / sum(1/sigma_j). The skfolio InverseVolatility documentation describes this normalized inverse-volatility rule. Our account simulation is additional original code, not a claim that normalization alone performs funded trading.
Volatility uses sample standard deviation, as documented in Python statistics. Use only completed history before the holding period. Do not include the return being predicted in its own weighting window, or estimate one full-sample volatility and apply it retrospectively to earlier dates.
Inverse volatility uses marginal volatility estimates rather than solving a general covariance-aware allocation problem. Normalization does not establish a universal equal-risk-contribution portfolio or guarantee smaller future drawdown. Correlation, estimation error and the chosen lookback remain relevant to interpreting the targets.
Work through the fixed synthetic histories
A’s eight session returns are [0.01,-0.01,0.02,0.01,-0.02,0.03,0.01,0.01]; B’s are [0.02,-0.02,0.04,-0.01,0.03,-0.01,0.02,-0.02]; C’s are eight zeros. Prices begin at 100 and are built by compounding these invented returns.
Rebalance at holding-session indices three, five and seven, using the preceding three returns each time. Hold through sessions three to seven, producing five account-return observations. The first two holding blocks have two sessions, the final block one. There are no external cash flows.
C has valid prices but zero measured volatility, so both rules exclude it throughout. At the first rebalance B’s historical volatility is twice A’s. Equal weights are one half each, while inverse weights are two thirds A and one third B.
| Rebalance index | Equal A/B targets | Inverse A/B targets |
|---|---|---|
| 3 | 0.500000 / 0.500000 | 0.666667 / 0.333333 |
| 5 | 0.500000 / 0.500000 | 0.559661 / 0.440339 |
| 7 | 0.500000 / 0.500000 | 0.452706 / 0.547294 |
Later inverse targets change because the historical window actually rolls. They are not a fixed initial allocation relabeled as dynamic. The sample changes a future return and asserts that the first target remains unchanged, checking this local information boundary.
Fund trades and fees together
Opening cash is 1000. Each rebalance charges 0.001 of absolute executed value on buys and sells, an invented ten-basis-point assumption. Fractional shares and immediate fills at the modeled boundary price are allowed. Opening prices equal the preceding close in this fixture; it does not demonstrate exact real market-on-open sizing or liquidity.
If V is post-fee invested capital, w the target and h current security values, solve V + 0.001*sum(abs(V*w-h)) = pretrade_equity. The script uses bisection, calculates share changes and then cash. Buying a full 1000 of assets before reserving fees would require borrowing and violate the stated account policy.
notional = sum(abs(new_shares[s] - shares[s]) * prices[s]
for s in basket)
cost = 0.001 * notional
cash = pretrade_equity - sum(new_shares[s] * prices[s]
for s in basket) - cost
At the initial all-cash rebalance, available investment is 1000/1.001, approximately 999.000999. Every rebalance asserts cash is nonnegative within 1e-8 and posttrade equity equals pretrade equity minus cost. This is a funded-account check beyond weight normalization.
Let actual weights drift
Between scheduled rebalances, keep share quantities fixed and mark them with the subsequent prices. Do not reset displayed weights to target every session without executing trades. After the first two-session block, equal-weight A drifts to approximately 0.492560 and B to 0.507440.
At index five, equal weighting buys approximately 0.073899 A shares and sells 0.070515 B shares to restore its target. Inverse weighting changes the target more substantially, selling approximately 0.994043 A and buying 0.944733 B. The complete output lists each rebalance’s targets, quantity changes and holding-block ending weights.
Turnover is all absolute executed value divided by opening NAV 1000, over this fixed five-session scored span. It is not annualized or halved. Costs are also included in the account path, so do not subtract them again from the printed net returns.
Compare the cost-aware results
| Metric | Equal weight | Inverse volatility |
|---|---|---|
| Ending value | 1023.715184 | 1018.583882 |
| Net return | 2.371518% | 1.858388% |
| Total fee amount | 1.029243 | 1.433830 |
| Gross traded value / opening NAV | 1.029243 | 1.433830 |
| Worst signed drawdown | −0.501480% | −0.664589% |
Fee and turnover numbers happen to match numerically because initial NAV is 1000 and fee rate 0.001; their units remain different. The inverse scheme turns over more and has a worse realized drawdown on this invented path. Its name does not guarantee superior realized risk or return.
The sample verifies ending values to an absolute tolerance of 1e-6, as well as funding, first targets and drift. Five synthetic account returns provide no credible long-run strategy conclusion. Preserve the whole path and the assumptions rather than publishing the larger number as evidence of investment merit.
Handle zero volatility and missing history explicitly
The boundary test supplies a constant-return stock and another with only two observations; both schemes produce an empty eligible set. A separate all-constant full simulation stays in cash at 1000 with zero fees. It does not divide by zero or normalize nonexistent risk weights.
Missing history is not a license to invent prices for existing holdings. If valuation or execution prices are unavailable, stop or apply a separately declared data policy. The fixture tests eligibility missingness with explicit helper inputs and uses complete prices for the funded account; it is not a general missing-market-data engine.
Execute and accept the comparison
Download the complete example. Use Python 3.12 or later and run python R627.py. It uses only the standard library and synthetic histories. Assertions cover shared eligibility, lagged targets, future-change invariance, cash fallback, funded costs, drift and the precise ending values above.
Accept when both methods use the same information and eligibility, targets map to actual funded trades, and costs and drift reconcile. Rebuild when basket, lookback, schedule, cost or execution policy changes. Preserve previous runs; this method neither validates a real product risk tool nor demonstrates market profitability.
For handoff, retain each historical window and eligibility decision beside the target, then connect the target to quantities, executed value, fees and cash. A recipient should reconstruct a rebalance without guessing whether fees were reserved before sizing. When actual execution cannot reach the target because of lots or unavailable quotes, preserve requested and achieved allocations separately. Do not smooth their difference away in a target-weight chart. Those execution gaps change the funded portfolio and need their own acceptance policy before the synthetic method is extended.
FAQ
Does a normalized weight vector include cash and fees?
No. A separate account execution process must fund orders and account for costs.
Should equal weight silently retain a zero-volatility stock?
Not in this comparison. Both methods deliberately share the declared eligibility rule.