ML predictions Phase 5: Monte Carlo contribution fix and milestone table
Fix contribution compounding: monthly contributions are now added to asset values before each GBM step so they grow with market returns, rather than being summed as a static lump at each period. Add year-by-year milestone table below the fan chart showing P10/P50/P90 portfolio values at each annual checkpoint up to the selected horizon. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
514d177b7b
commit
2940b120e0
3 changed files with 56 additions and 3 deletions
|
|
@ -100,14 +100,15 @@ def run_monte_carlo(
|
|||
for sim in range(n_sims):
|
||||
asset_values = current_values.copy()
|
||||
for t in range(n_months):
|
||||
# Add monthly contribution before GBM step so it compounds
|
||||
if monthly_contribution > 0:
|
||||
asset_values = asset_values + weights * monthly_contribution
|
||||
Z = rng.standard_normal(n_assets)
|
||||
corr_Z = L @ Z
|
||||
# GBM step for each asset
|
||||
asset_values = asset_values * np.exp(
|
||||
(mus - 0.5 * sigmas ** 2) * DT + sigmas * np.sqrt(DT) * corr_Z
|
||||
)
|
||||
port_val = float(asset_values.sum()) + monthly_contribution * (t + 1)
|
||||
portfolio_paths[sim, t] = max(0.0, port_val)
|
||||
portfolio_paths[sim, t] = max(0.0, float(asset_values.sum()))
|
||||
|
||||
# Compute percentile paths
|
||||
pcts = {
|
||||
|
|
@ -122,6 +123,18 @@ def run_monte_carlo(
|
|||
prob_gain = float(np.mean(final_values > total_value))
|
||||
expected_value = float(np.median(final_values))
|
||||
|
||||
# Year-by-year milestones (at each 12-month boundary)
|
||||
milestones = []
|
||||
for yr in range(1, years + 1):
|
||||
idx = min(yr * 12 - 1, n_months - 1)
|
||||
milestones.append({
|
||||
"year": yr,
|
||||
"date": future_dates[idx],
|
||||
"p10": round(float(np.percentile(portfolio_paths[:, idx], 10)), 2),
|
||||
"p50": round(float(np.percentile(portfolio_paths[:, idx], 50)), 2),
|
||||
"p90": round(float(np.percentile(portfolio_paths[:, idx], 90)), 2),
|
||||
})
|
||||
|
||||
return {
|
||||
"dates": future_dates,
|
||||
"percentiles": {
|
||||
|
|
@ -131,5 +144,6 @@ def run_monte_carlo(
|
|||
"current_value": round(total_value, 2),
|
||||
"expected_value": round(expected_value, 2),
|
||||
"probability_of_gain": round(prob_gain, 3),
|
||||
"milestones": milestones,
|
||||
"insufficient_data": False,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue