Last active
July 10, 2025 17:24
-
-
Save st1vms/2c6abb8c17aeab9a42ae5c23ed1a3ae2 to your computer and use it in GitHub Desktop.
Fund calculator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Requirements: pandas matplotlib | |
import pandas as pd | |
import numpy as np | |
import matplotlib.pyplot as plt | |
from matplotlib import ticker | |
# Parameters | |
MONTHLY_INVESTMENT = 100 | |
MONTHLY_RATES = [0.005] # All the monthly rates to simulate | |
SIMULATION_YEARS = 10 # Total number of years to simulate future value | |
# List of (loss_probability, [loss_percentage1, loss_perc2, ...]) | |
LOSS_MAP = [(0.003, [0.08]), (0.02, [0.03]), (0.1, [0.01])] | |
LOSS_MAP = sorted(LOSS_MAP, key=lambda v: v[0]) | |
def plot_investement( | |
monthly_investment: float, | |
monthly_rates: list[float], | |
simulation_years: int, | |
loss_map: list[tuple[float, list[float]]], | |
seed: int = 42, | |
): | |
""" | |
Simulate and plot the future value of monthly investments with probabilistic losses. | |
Args: | |
monthly_investment (float): Fixed amount invested each month. | |
monthly_rates (list of float): List of expected monthly return rates to simulate. | |
simulation_years (int): Number of years to run the simulation. | |
loss_map (list of tuples): List of (loss_probability, [loss_percentages]) events. | |
seed (int, optional): Random seed for reproducibility. Defaults to 42. | |
Behavior: | |
For each monthly rate, simulates month-by-month: | |
- Checks if a loss event occurs based on probabilities. | |
- Applies loss if event occurs. | |
- Applies monthly gain and adds new investment. | |
- Records the future value over time. | |
Produces a plot comparing investment value and total invested capital. | |
""" | |
# Build cumulative loss map | |
cumulative_loss_map = [] | |
cumulative_prob = 0.0 | |
for prob, losses in loss_map: | |
cumulative_prob += prob | |
cumulative_loss_map.append((cumulative_prob, losses)) | |
# Set a seed for reproducibility | |
np.random.seed(seed) | |
# Simulate the future values | |
results = {} | |
# Simulate each monthly rate | |
for r in monthly_rates: | |
fv = [] | |
# We start the first month with a base investment | |
monthly_fv = monthly_investment | |
for y in range(simulation_years): | |
for m in range(12): | |
# Simulate probability of loss | |
prob = np.random.rand() | |
for threshold, loss_list in cumulative_loss_map: | |
if prob < threshold: | |
loss = np.random.choice(loss_list) | |
monthly_fv -= monthly_fv * loss | |
break | |
# Monthly win + new investement | |
monthly_fv += monthly_fv * r + monthly_investment | |
fv.append(monthly_fv) | |
results[f"Monthly return, price rate: {int(r * 100)}%"] = fv | |
tot = [] | |
total_invested = 0 | |
for y in range(simulation_years): | |
for m in range(12): | |
total_invested += monthly_investment | |
tot.append(total_invested) | |
results["Total Invested"] = tot | |
pd.options.display.float_format = "{:,.2f}".format | |
df = pd.DataFrame(results) | |
df.index.name = "Months" | |
print(df.round(2)) | |
ax = df.plot() | |
ax.set_xlabel("Months") | |
ax.set_ylabel("Monthly Future Value (€)") | |
ax.set_title( | |
f"""€{monthly_investment}/Month, Total Invested : {results["Total Invested"][-1]} in {simulation_years} years.""" | |
) | |
ax.grid(True) | |
ax.yaxis.set_major_formatter( | |
ticker.StrMethodFormatter("{x:,.0f}") | |
) # Adds commas and no decimals | |
plt.tight_layout() | |
plt.show() | |
if __name__ == "__main__": | |
plot_investement( | |
MONTHLY_INVESTMENT, MONTHLY_RATES, SIMULATION_YEARS, LOSS_MAP, seed=42 | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment