| """ |
| Monte Carlo Simulation — Stress testing the strategy by shuffling trade sequences. |
| """ |
| import numpy as np |
| import pandas as pd |
| from typing import List |
|
|
| def run_monte_carlo(trades: List[dict], initial_capital: float, iterations: int = 1000): |
| """ |
| Run Monte Carlo simulation on a list of historical trades. |
| |
| Args: |
| trades: List of trade dictionaries with 'pnl' and 'entry_price'. |
| initial_capital: Starting capital. |
| iterations: Number of simulations. |
| """ |
| if not trades: |
| return {"error": "No trades to simulate"} |
|
|
| pnls = [t["pnl"] for t in trades] |
| results = [] |
|
|
| for _ in range(iterations): |
| |
| shuffled_pnls = np.random.choice(pnls, size=len(pnls), replace=True) |
| |
| equity = initial_capital |
| equity_curve = [initial_capital] |
| max_dd = 0 |
| peak = initial_capital |
| |
| for pnl in shuffled_pnls: |
| equity += pnl |
| equity_curve.append(equity) |
| if equity > peak: |
| peak = equity |
| dd = (peak - equity) / peak |
| if dd > max_dd: |
| max_dd = dd |
| |
| results.append({ |
| "final_equity": equity, |
| "max_drawdown": max_dd * 100, |
| "return_pct": (equity / initial_capital - 1) * 100 |
| }) |
|
|
| df_results = pd.DataFrame(results) |
| |
| stats = { |
| "avg_return": df_results["return_pct"].mean(), |
| "median_return": df_results["return_pct"].median(), |
| "worst_case_return": df_results["return_pct"].min(), |
| "best_case_return": df_results["return_pct"].max(), |
| "avg_drawdown": df_results["max_drawdown"].mean(), |
| "max_drawdown_95th": df_results["max_drawdown"].quantile(0.95), |
| "ruin_probability": (df_results["final_equity"] < initial_capital * 0.5).mean() * 100 |
| } |
| |
| return stats, df_results |
|
|
| def print_mc_report(stats): |
| print("\n" + "="*40) |
| print(" MONTE CARLO STRESS TEST") |
| print("="*40) |
| print(f"Avg Return: {stats['avg_return']:.2f}%") |
| print(f"Median Return: {stats['median_return']:.2f}%") |
| print(f"Best Case: {stats['best_case_return']:.2f}%") |
| print(f"Worst Case: {stats['worst_case_return']:.2f}%") |
| print(f"Avg Drawdown: {stats['avg_drawdown']:.2f}%") |
| print(f"Max DD (95% CI): {stats['max_drawdown_95th']:.2f}%") |
| print(f"Prob. of Ruin: {stats['ruin_probability']:.1f}%") |
| print("="*40) |
|
|