|
|
import json
|
|
|
import numpy as np
|
|
|
import pandas as pd
|
|
|
import matplotlib.pyplot as plt
|
|
|
import pickle
|
|
|
|
|
|
def monte_carlo_simulation(portfolio_data, scenario_data, num_simulations=10000):
|
|
|
"""
|
|
|
Performs a Monte Carlo simulation on a portfolio based on market scenarios.
|
|
|
|
|
|
Args:
|
|
|
portfolio_data (dict): Dictionary of portfolio data.
|
|
|
scenario_data (dict): Dictionary of market scenario data.
|
|
|
num_simulations (int, optional): The number of simulations. Defaults to 10000.
|
|
|
|
|
|
Returns:
|
|
|
dict: A dictionary containing simulation results for each scenario.
|
|
|
"""
|
|
|
scenarios = scenario_data["market_scenarios"]
|
|
|
results = {}
|
|
|
|
|
|
for scenario_key, scenario_details in scenarios.items():
|
|
|
scenario_name = scenario_details["name"]
|
|
|
sector_impacts = scenario_details.get("sector_impact", {})
|
|
|
results[scenario_name] = {
|
|
|
"portfolio_values": [],
|
|
|
"average_return": 0,
|
|
|
"std_dev_return": 0,
|
|
|
"percentiles": {},
|
|
|
}
|
|
|
|
|
|
for _ in range(num_simulations):
|
|
|
portfolio_value = 0
|
|
|
for asset_name, asset_details in portfolio_data["assets"].items():
|
|
|
sector = asset_details["sector"]
|
|
|
quantity = asset_details["quantity"]
|
|
|
initial_price = asset_details["initial_price"]
|
|
|
|
|
|
price_change_percentage = 0
|
|
|
if sector in sector_impacts:
|
|
|
price_change_percentage = np.random.normal(
|
|
|
loc=sector_impacts[sector] / 100, scale=0.1
|
|
|
)
|
|
|
|
|
|
|
|
|
new_price = initial_price * (1 + price_change_percentage)
|
|
|
|
|
|
portfolio_value += new_price * quantity
|
|
|
results[scenario_name]["portfolio_values"].append(portfolio_value)
|
|
|
|
|
|
|
|
|
portfolio_values = results[scenario_name]["portfolio_values"]
|
|
|
initial_portfolio_value = sum(
|
|
|
asset["quantity"] * asset["initial_price"] for asset in portfolio_data["assets"].values()
|
|
|
)
|
|
|
returns = [
|
|
|
(value - initial_portfolio_value) / initial_portfolio_value
|
|
|
for value in portfolio_values
|
|
|
]
|
|
|
|
|
|
results[scenario_name]["average_return"] = np.mean(returns)
|
|
|
results[scenario_name]["std_dev_return"] = np.std(returns)
|
|
|
results[scenario_name]["percentiles"] = {
|
|
|
5: np.percentile(returns, 5),
|
|
|
25: np.percentile(returns, 25),
|
|
|
50: np.percentile(returns, 50),
|
|
|
75: np.percentile(returns, 75),
|
|
|
95: np.percentile(returns, 95),
|
|
|
}
|
|
|
|
|
|
return results
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
with open("output_files/scenario.json") as f:
|
|
|
scenario_data = json.load(f)
|
|
|
|
|
|
with open("output_files/portfolio.json") as f:
|
|
|
portfolio_data = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
def load_dataframes(filename="output_files/saved_dataframes.pkl"):
|
|
|
try:
|
|
|
with open(filename, 'rb') as file:
|
|
|
saved_dataframes = pickle.load(file)
|
|
|
print(f"DataFrames successfully loaded from {filename}.")
|
|
|
return saved_dataframes
|
|
|
except FileNotFoundError:
|
|
|
print(f"File {filename} not found.")
|
|
|
return None
|
|
|
|
|
|
saved_dataframes = load_dataframes()
|
|
|
|
|
|
|
|
|
scenario_results = {}
|
|
|
|
|
|
|
|
|
for scenario_name, scenario_details in scenario_data["market_scenarios"].items():
|
|
|
impacted_sectors = scenario_details["sector_impact"]
|
|
|
|
|
|
|
|
|
relevant_assets = [
|
|
|
symbol
|
|
|
for symbol, details in portfolio_data["assets"].items()
|
|
|
if details["sector"] in impacted_sectors
|
|
|
]
|
|
|
|
|
|
|
|
|
sector_magnitudes = {}
|
|
|
for symbol in relevant_assets:
|
|
|
df = saved_dataframes[symbol]
|
|
|
sector = portfolio_data["assets"][symbol]["sector"]
|
|
|
|
|
|
|
|
|
magnitude = abs(df["Close"].iloc[-2] - df["Close"].iloc[-1])
|
|
|
|
|
|
|
|
|
if sector not in sector_magnitudes:
|
|
|
sector_magnitudes[sector] = 0
|
|
|
sector_magnitudes[sector] += magnitude
|
|
|
|
|
|
|
|
|
aggregated_magnitude = sum(sector_magnitudes.values())
|
|
|
|
|
|
|
|
|
scenario_results[scenario_name] = {
|
|
|
"individual_magnitudes": sector_magnitudes,
|
|
|
"aggregated_magnitude": aggregated_magnitude,
|
|
|
}
|
|
|
|
|
|
|
|
|
for scenario_name, results in scenario_results.items():
|
|
|
print(f"\nScenario: {scenario_name}")
|
|
|
print("Individual Sector Magnitudes:")
|
|
|
for sector, magnitude in results["individual_magnitudes"].items():
|
|
|
print(f" {sector}: {magnitude:.2f}")
|
|
|
print(f"Aggregated Magnitude: {results['aggregated_magnitude']:.2f}")
|
|
|
|
|
|
|
|
|
for scenario_id, results in scenario_results.items():
|
|
|
|
|
|
scenario_data["market_scenarios"][scenario_id]["sector_impact"] = results["individual_magnitudes"]
|
|
|
|
|
|
scenario_data["market_scenarios"][scenario_id]["aggregated_magnitude"] = results["aggregated_magnitude"]
|
|
|
|
|
|
|
|
|
output_file_path = "output_files/updated_scenario_data.json"
|
|
|
with open(output_file_path, "w") as file:
|
|
|
json.dump(scenario_data, file, indent=4)
|
|
|
|
|
|
print(f"Updated scenario data saved to '{output_file_path}' successfully!")
|
|
|
|
|
|
|
|
|
simulation_results = monte_carlo_simulation(portfolio_data, scenario_data)
|
|
|
|
|
|
|
|
|
simulation_results_file = "output_files/simulation_results.json"
|
|
|
with open(simulation_results_file, "w") as file:
|
|
|
json.dump(simulation_results, file, indent=4)
|
|
|
|
|
|
print(f"Simulation results saved to '{simulation_results_file}' successfully!")
|
|
|
|
|
|
|
|
|
for scenario_name, results in simulation_results.items():
|
|
|
print(f"Scenario: {scenario_name}")
|
|
|
print(f" Average Return: {results['average_return']:.4f}")
|
|
|
print(f" Std Dev Return: {results['std_dev_return']:.4f}")
|
|
|
print(" Return Percentiles:")
|
|
|
for percentile, value in results["percentiles"].items():
|
|
|
print(f" {percentile}th: {value:.4f}")
|
|
|
print("-" * 40)
|
|
|
|