diff --git a/js/monte-carlo-modal.js b/js/monte-carlo-modal.js
index 2c1e25e..8e25d64 100644
--- a/js/monte-carlo-modal.js
+++ b/js/monte-carlo-modal.js
@@ -59,11 +59,18 @@ function displayMonteCarloModal(strategy, index, results) {
-
Expected Return
-
- ${expectedReturn > 0 ? '+' : ''}${expectedReturn.toFixed(2)}%
+
Max Drawdown (Worst)
+
+ ${results.statistics.drawdown.percentile95.toFixed(2)}%
-
Mean final equity
+
95th percentile (worst 5%)
+
+
+
Max Drawdown (Median)
+
+ ${results.statistics.drawdown.median.toFixed(2)}%
+
+
Typical drawdown scenario
Risk of Ruin
@@ -75,96 +82,96 @@ function displayMonteCarloModal(strategy, index, results) {
${results.iterations.toLocaleString()}
Completed in ${results.executionTime.toFixed(0)}ms
-
-
Std Deviation
-
$${stats.stdDev.toFixed(2)}
-
Variability measure
-
-
+
-
Equity Distribution
+
📉 Drawdown Distribution
- Distribution of final equity across ${results.iterations.toLocaleString()} randomized trade sequences
+ Distribution of maximum drawdown across ${results.iterations.toLocaleString()} randomized trade sequences
-
+
+
+
💡 Understanding Monte Carlo Results
+
+ Why focus on drawdown? When shuffling trades, the final equity stays nearly constant
+ (sum of trades doesn't change), but the drawdown varies significantly based on trade order.
+
+
+ Key Insight: A robust strategy should show consistent low drawdowns across
+ different trade sequences. High drawdown variance indicates the strategy's performance is highly dependent
+ on lucky trade timing.
+
+
+
+
-
Percentile Analysis
+
Drawdown Percentile Analysis
| Percentile |
- Final Equity |
- Return |
+ Max Drawdown |
Interpretation |
- | 5th (Worst 5%) |
- $${stats.percentile5.toFixed(2)} |
-
- ${((stats.percentile5 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile5 - 10000) / 10000 * 100).toFixed(2)}%
- |
- Downside risk scenario |
+ 5th (Best 5%) |
+ ${results.statistics.drawdown.percentile5.toFixed(2)}% |
+ Best-case drawdown scenario |
| 25th |
- $${stats.percentile25.toFixed(2)} |
-
- ${((stats.percentile25 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile25 - 10000) / 10000 * 100).toFixed(2)}%
- |
- Below average outcome |
+ ${results.statistics.drawdown.percentile25.toFixed(2)}% |
+ Better than average |
| 50th (Median) |
- $${stats.percentile50.toFixed(2)} |
-
- ${((stats.percentile50 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile50 - 10000) / 10000 * 100).toFixed(2)}%
- |
- Typical outcome |
+ ${results.statistics.drawdown.median.toFixed(2)}% |
+ Typical drawdown |
| 75th |
- $${stats.percentile75.toFixed(2)} |
-
- ${((stats.percentile75 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile75 - 10000) / 10000 * 100).toFixed(2)}%
- |
- Above average outcome |
+ ${results.statistics.drawdown.percentile75.toFixed(2)}% |
+ Worse than average |
- | 95th (Best 5%) |
- $${stats.percentile95.toFixed(2)} |
-
- ${((stats.percentile95 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile95 - 10000) / 10000 * 100).toFixed(2)}%
- |
- Upside potential |
+ 95th (Worst 5%) |
+ ${results.statistics.drawdown.percentile95.toFixed(2)}% |
+ Worst-case drawdown scenario |
-
+
-
Confidence Intervals
+
Drawdown Statistics Summary
-
90% Confidence Range
+
Average Drawdown
- $${results.confidence.range90.lower.toFixed(2)} - $${results.confidence.range90.upper.toFixed(2)}
+ ${results.statistics.drawdown.mean.toFixed(2)}%
-
90% of outcomes fall within this range
+
Mean across all simulations
-
50% Confidence Range
+
Drawdown Std Dev
- $${results.confidence.range50.lower.toFixed(2)} - $${results.confidence.range50.upper.toFixed(2)}
+ ${results.statistics.drawdown.stdDev.toFixed(2)}%
-
50% of outcomes fall within this range
+
Variability in drawdown outcomes
+
+
+
Expected Return
+
+ ${expectedReturn > 0 ? '+' : ''}${expectedReturn.toFixed(2)}%
+
+
Consistent across simulations
@@ -203,32 +210,34 @@ function drawMonteCarloHistogram(results, stats) {
const ctx = canvas.getContext('2d');
- // Create histogram bins
+ // Create histogram bins for DRAWDOWN distribution
const numBins = 30;
- const min = Math.min(...results.distribution);
- const max = Math.max(...results.distribution);
+ const drawdownData = results.drawdownDistribution;
+ const min = Math.min(...drawdownData);
+ const max = Math.max(...drawdownData);
const binWidth = (max - min) / numBins;
const bins = new Array(numBins).fill(0);
const binLabels = [];
for (let i = 0; i < numBins; i++) {
- binLabels.push((min + i * binWidth).toFixed(0));
+ binLabels.push((min + i * binWidth).toFixed(1));
}
- // Fill bins
- results.distribution.forEach(value => {
+ // Fill bins with drawdown data
+ drawdownData.forEach(value => {
const binIndex = Math.min(Math.floor((value - min) / binWidth), numBins - 1);
bins[binIndex]++;
});
- // Create gradient colors based on value
+ // Create gradient colors based on drawdown value (GREEN = low DD, RED = high DD)
+ const ddStats = results.statistics.drawdown;
const backgroundColors = bins.map((_, i) => {
const value = min + (i + 0.5) * binWidth;
- if (value < stats.percentile5) return 'rgba(245, 101, 101, 0.7)'; // Red for worst 5%
- if (value < stats.percentile25) return 'rgba(237, 137, 54, 0.7)'; // Orange
- if (value < stats.percentile75) return 'rgba(72, 187, 120, 0.7)'; // Green
- return 'rgba(56, 178, 172, 0.7)'; // Teal for best 25%
+ if (value < ddStats.percentile25) return 'rgba(72, 187, 120, 0.7)'; // Green for best 25%
+ if (value < ddStats.percentile50) return 'rgba(56, 178, 172, 0.7)'; // Teal
+ if (value < ddStats.percentile75) return 'rgba(237, 137, 54, 0.7)'; // Orange
+ return 'rgba(245, 101, 101, 0.7)'; // Red for worst 25%
});
new Chart(ctx, {
@@ -258,7 +267,7 @@ function drawMonteCarloHistogram(results, stats) {
title: function (context) {
const binStart = parseFloat(context[0].label);
const binEnd = binStart + binWidth;
- return `$${binStart.toFixed(0)} - $${binEnd.toFixed(0)}`;
+ return `${binStart.toFixed(1)}% - ${binEnd.toFixed(1)}%`;
},
label: function (context) {
const percentage = (context.parsed.y / results.iterations * 100).toFixed(1);
@@ -268,15 +277,15 @@ function drawMonteCarloHistogram(results, stats) {
},
annotation: {
annotations: {
- meanLine: {
+ medianLine: {
type: 'line',
- xMin: ((stats.mean - min) / binWidth),
- xMax: ((stats.mean - min) / binWidth),
+ xMin: ((ddStats.median - min) / binWidth),
+ xMax: ((ddStats.median - min) / binWidth),
borderColor: '#667eea',
borderWidth: 2,
borderDash: [5, 5],
label: {
- content: 'Mean',
+ content: 'Median',
enabled: true,
position: 'top'
}
@@ -306,7 +315,7 @@ function drawMonteCarloHistogram(results, stats) {
},
title: {
display: true,
- text: 'Final Equity ($)',
+ text: 'Maximum Drawdown (%)',
color: '#a0aec0'
}
}
diff --git a/js/monte-carlo.js b/js/monte-carlo.js
index 670a842..876b1f9 100644
--- a/js/monte-carlo.js
+++ b/js/monte-carlo.js
@@ -21,9 +21,7 @@ class MonteCarloSimulation {
* @returns {Object} Statistical results
*/
run() {
- console.log(`🎲 Running Monte Carlo simulation (${this.iterations} iterations)...`);
const startTime = performance.now();
-
const results = [];
for (let i = 0; i < this.iterations; i++) {
@@ -42,8 +40,6 @@ class MonteCarloSimulation {
const statistics = this.calculateStatistics(results);
const endTime = performance.now();
- console.log(`✅ Monte Carlo complete in ${(endTime - startTime).toFixed(0)}ms`);
-
return {
iterations: this.iterations,
statistics,
@@ -138,6 +134,9 @@ class MonteCarloSimulation {
min: Math.min(...drawdowns),
max: Math.max(...drawdowns),
percentile5: this.getPercentile(drawdowns, 5),
+ percentile25: this.getPercentile(drawdowns, 25),
+ percentile50: this.getPercentile(drawdowns, 50),
+ percentile75: this.getPercentile(drawdowns, 75),
percentile95: this.getPercentile(drawdowns, 95)
}
};
@@ -260,12 +259,14 @@ class MonteCarloSimulation {
throw new Error('Strategy must have backtest results with trades');
}
- return strategy.metrics.trades.map(trade => ({
+ const trades = strategy.metrics.trades.map(trade => ({
profit: trade.profit,
type: trade.type,
entry: trade.entry,
exit: trade.exit
}));
+
+ return trades;
}
}