mirror of
https://github.com/FxPouya/FxMathQuantWebApp.git
synced 2026-08-15 02:18:04 +00:00
Add files via upload
This commit is contained in:
+77
-68
@@ -59,11 +59,18 @@ function displayMonteCarloModal(strategy, index, results) {
|
|||||||
<!-- Summary Statistics -->
|
<!-- Summary Statistics -->
|
||||||
<div class="mc-summary">
|
<div class="mc-summary">
|
||||||
<div class="mc-stat-card">
|
<div class="mc-stat-card">
|
||||||
<div class="mc-label">Expected Return</div>
|
<div class="mc-label">Max Drawdown (Worst)</div>
|
||||||
<div class="mc-value ${expectedReturn >= 0 ? 'positive' : 'negative'}">
|
<div class="mc-value negative">
|
||||||
${expectedReturn > 0 ? '+' : ''}${expectedReturn.toFixed(2)}%
|
${results.statistics.drawdown.percentile95.toFixed(2)}%
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-sublabel">Mean final equity</div>
|
<div class="mc-sublabel">95th percentile (worst 5%)</div>
|
||||||
|
</div>
|
||||||
|
<div class="mc-stat-card">
|
||||||
|
<div class="mc-label">Max Drawdown (Median)</div>
|
||||||
|
<div class="mc-value mc-warning">
|
||||||
|
${results.statistics.drawdown.median.toFixed(2)}%
|
||||||
|
</div>
|
||||||
|
<div class="mc-sublabel">Typical drawdown scenario</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-stat-card">
|
<div class="mc-stat-card">
|
||||||
<div class="mc-label">Risk of Ruin</div>
|
<div class="mc-label">Risk of Ruin</div>
|
||||||
@@ -75,96 +82,96 @@ function displayMonteCarloModal(strategy, index, results) {
|
|||||||
<div class="mc-value">${results.iterations.toLocaleString()}</div>
|
<div class="mc-value">${results.iterations.toLocaleString()}</div>
|
||||||
<div class="mc-sublabel">Completed in ${results.executionTime.toFixed(0)}ms</div>
|
<div class="mc-sublabel">Completed in ${results.executionTime.toFixed(0)}ms</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-stat-card">
|
|
||||||
<div class="mc-label">Std Deviation</div>
|
|
||||||
<div class="mc-value">$${stats.stdDev.toFixed(2)}</div>
|
|
||||||
<div class="mc-sublabel">Variability measure</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Equity Distribution Histogram -->
|
<!-- Drawdown Distribution Histogram -->
|
||||||
<div class="chart-section">
|
<div class="chart-section">
|
||||||
<h3>Equity Distribution</h3>
|
<h3>📉 Drawdown Distribution</h3>
|
||||||
<p style="color: #a0aec0; margin-bottom: 15px;">
|
<p style="color: #a0aec0; margin-bottom: 15px;">
|
||||||
Distribution of final equity across ${results.iterations.toLocaleString()} randomized trade sequences
|
Distribution of maximum drawdown across ${results.iterations.toLocaleString()} randomized trade sequences
|
||||||
</p>
|
</p>
|
||||||
<canvas id="mc-histogram" style="max-height: 300px;"></canvas>
|
<canvas id="mc-histogram" style="max-height: 300px;"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Percentile Statistics -->
|
<!-- Educational Note -->
|
||||||
|
<div class="mc-explanation" style="margin-top: 20px; background: rgba(59, 130, 246, 0.1); border-left: 3px solid #3b82f6; padding: 15px;">
|
||||||
|
<h4 style="margin-bottom: 10px; color: #60a5fa;">💡 Understanding Monte Carlo Results</h4>
|
||||||
|
<p style="margin-bottom: 8px;">
|
||||||
|
<strong>Why focus on drawdown?</strong> When shuffling trades, the final equity stays nearly constant
|
||||||
|
(sum of trades doesn't change), but the <strong>drawdown varies significantly</strong> based on trade order.
|
||||||
|
</p>
|
||||||
|
<p style="margin-bottom: 0;">
|
||||||
|
<strong>Key Insight:</strong> A robust strategy should show <strong>consistent low drawdowns</strong> across
|
||||||
|
different trade sequences. High drawdown variance indicates the strategy's performance is highly dependent
|
||||||
|
on lucky trade timing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Drawdown Percentile Statistics -->
|
||||||
<div class="mc-percentiles">
|
<div class="mc-percentiles">
|
||||||
<h3>Percentile Analysis</h3>
|
<h3>Drawdown Percentile Analysis</h3>
|
||||||
<table class="mc-percentile-table">
|
<table class="mc-percentile-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Percentile</th>
|
<th>Percentile</th>
|
||||||
<th>Final Equity</th>
|
<th>Max Drawdown</th>
|
||||||
<th>Return</th>
|
|
||||||
<th>Interpretation</th>
|
<th>Interpretation</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>5th (Worst 5%)</strong></td>
|
<td><strong>5th (Best 5%)</strong></td>
|
||||||
<td>$${stats.percentile5.toFixed(2)}</td>
|
<td class="positive">${results.statistics.drawdown.percentile5.toFixed(2)}%</td>
|
||||||
<td class="${((stats.percentile5 - 10000) / 10000 * 100) >= 0 ? 'positive' : 'negative'}">
|
<td>Best-case drawdown scenario</td>
|
||||||
${((stats.percentile5 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile5 - 10000) / 10000 * 100).toFixed(2)}%
|
|
||||||
</td>
|
|
||||||
<td>Downside risk scenario</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>25th</strong></td>
|
<td><strong>25th</strong></td>
|
||||||
<td>$${stats.percentile25.toFixed(2)}</td>
|
<td>${results.statistics.drawdown.percentile25.toFixed(2)}%</td>
|
||||||
<td class="${((stats.percentile25 - 10000) / 10000 * 100) >= 0 ? 'positive' : 'negative'}">
|
<td>Better than average</td>
|
||||||
${((stats.percentile25 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile25 - 10000) / 10000 * 100).toFixed(2)}%
|
|
||||||
</td>
|
|
||||||
<td>Below average outcome</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="highlight-row">
|
<tr class="highlight-row">
|
||||||
<td><strong>50th (Median)</strong></td>
|
<td><strong>50th (Median)</strong></td>
|
||||||
<td>$${stats.percentile50.toFixed(2)}</td>
|
<td>${results.statistics.drawdown.median.toFixed(2)}%</td>
|
||||||
<td class="${((stats.percentile50 - 10000) / 10000 * 100) >= 0 ? 'positive' : 'negative'}">
|
<td>Typical drawdown</td>
|
||||||
${((stats.percentile50 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile50 - 10000) / 10000 * 100).toFixed(2)}%
|
|
||||||
</td>
|
|
||||||
<td>Typical outcome</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>75th</strong></td>
|
<td><strong>75th</strong></td>
|
||||||
<td>$${stats.percentile75.toFixed(2)}</td>
|
<td class="mc-warning">${results.statistics.drawdown.percentile75.toFixed(2)}%</td>
|
||||||
<td class="${((stats.percentile75 - 10000) / 10000 * 100) >= 0 ? 'positive' : 'negative'}">
|
<td>Worse than average</td>
|
||||||
${((stats.percentile75 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile75 - 10000) / 10000 * 100).toFixed(2)}%
|
|
||||||
</td>
|
|
||||||
<td>Above average outcome</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>95th (Best 5%)</strong></td>
|
<td><strong>95th (Worst 5%)</strong></td>
|
||||||
<td>$${stats.percentile95.toFixed(2)}</td>
|
<td class="negative">${results.statistics.drawdown.percentile95.toFixed(2)}%</td>
|
||||||
<td class="${((stats.percentile95 - 10000) / 10000 * 100) >= 0 ? 'positive' : 'negative'}">
|
<td>Worst-case drawdown scenario</td>
|
||||||
${((stats.percentile95 - 10000) / 10000 * 100) > 0 ? '+' : ''}${((stats.percentile95 - 10000) / 10000 * 100).toFixed(2)}%
|
|
||||||
</td>
|
|
||||||
<td>Upside potential</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Confidence Intervals -->
|
<!-- Drawdown Statistics Summary -->
|
||||||
<div class="mc-confidence">
|
<div class="mc-confidence">
|
||||||
<h3>Confidence Intervals</h3>
|
<h3>Drawdown Statistics Summary</h3>
|
||||||
<div class="mc-confidence-grid">
|
<div class="mc-confidence-grid">
|
||||||
<div class="mc-confidence-card">
|
<div class="mc-confidence-card">
|
||||||
<div class="mc-confidence-label">90% Confidence Range</div>
|
<div class="mc-confidence-label">Average Drawdown</div>
|
||||||
<div class="mc-confidence-value">
|
<div class="mc-confidence-value">
|
||||||
$${results.confidence.range90.lower.toFixed(2)} - $${results.confidence.range90.upper.toFixed(2)}
|
${results.statistics.drawdown.mean.toFixed(2)}%
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-confidence-sublabel">90% of outcomes fall within this range</div>
|
<div class="mc-confidence-sublabel">Mean across all simulations</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-confidence-card">
|
<div class="mc-confidence-card">
|
||||||
<div class="mc-confidence-label">50% Confidence Range</div>
|
<div class="mc-confidence-label">Drawdown Std Dev</div>
|
||||||
<div class="mc-confidence-value">
|
<div class="mc-confidence-value">
|
||||||
$${results.confidence.range50.lower.toFixed(2)} - $${results.confidence.range50.upper.toFixed(2)}
|
${results.statistics.drawdown.stdDev.toFixed(2)}%
|
||||||
</div>
|
</div>
|
||||||
<div class="mc-confidence-sublabel">50% of outcomes fall within this range</div>
|
<div class="mc-confidence-sublabel">Variability in drawdown outcomes</div>
|
||||||
|
</div>
|
||||||
|
<div class="mc-confidence-card">
|
||||||
|
<div class="mc-confidence-label">Expected Return</div>
|
||||||
|
<div class="mc-confidence-value ${expectedReturn >= 0 ? 'positive' : 'negative'}">
|
||||||
|
${expectedReturn > 0 ? '+' : ''}${expectedReturn.toFixed(2)}%
|
||||||
|
</div>
|
||||||
|
<div class="mc-confidence-sublabel">Consistent across simulations</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -203,32 +210,34 @@ function drawMonteCarloHistogram(results, stats) {
|
|||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
// Create histogram bins
|
// Create histogram bins for DRAWDOWN distribution
|
||||||
const numBins = 30;
|
const numBins = 30;
|
||||||
const min = Math.min(...results.distribution);
|
const drawdownData = results.drawdownDistribution;
|
||||||
const max = Math.max(...results.distribution);
|
const min = Math.min(...drawdownData);
|
||||||
|
const max = Math.max(...drawdownData);
|
||||||
const binWidth = (max - min) / numBins;
|
const binWidth = (max - min) / numBins;
|
||||||
|
|
||||||
const bins = new Array(numBins).fill(0);
|
const bins = new Array(numBins).fill(0);
|
||||||
const binLabels = [];
|
const binLabels = [];
|
||||||
|
|
||||||
for (let i = 0; i < numBins; i++) {
|
for (let i = 0; i < numBins; i++) {
|
||||||
binLabels.push((min + i * binWidth).toFixed(0));
|
binLabels.push((min + i * binWidth).toFixed(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill bins
|
// Fill bins with drawdown data
|
||||||
results.distribution.forEach(value => {
|
drawdownData.forEach(value => {
|
||||||
const binIndex = Math.min(Math.floor((value - min) / binWidth), numBins - 1);
|
const binIndex = Math.min(Math.floor((value - min) / binWidth), numBins - 1);
|
||||||
bins[binIndex]++;
|
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 backgroundColors = bins.map((_, i) => {
|
||||||
const value = min + (i + 0.5) * binWidth;
|
const value = min + (i + 0.5) * binWidth;
|
||||||
if (value < stats.percentile5) return 'rgba(245, 101, 101, 0.7)'; // Red for worst 5%
|
if (value < ddStats.percentile25) return 'rgba(72, 187, 120, 0.7)'; // Green for best 25%
|
||||||
if (value < stats.percentile25) return 'rgba(237, 137, 54, 0.7)'; // Orange
|
if (value < ddStats.percentile50) return 'rgba(56, 178, 172, 0.7)'; // Teal
|
||||||
if (value < stats.percentile75) return 'rgba(72, 187, 120, 0.7)'; // Green
|
if (value < ddStats.percentile75) return 'rgba(237, 137, 54, 0.7)'; // Orange
|
||||||
return 'rgba(56, 178, 172, 0.7)'; // Teal for best 25%
|
return 'rgba(245, 101, 101, 0.7)'; // Red for worst 25%
|
||||||
});
|
});
|
||||||
|
|
||||||
new Chart(ctx, {
|
new Chart(ctx, {
|
||||||
@@ -258,7 +267,7 @@ function drawMonteCarloHistogram(results, stats) {
|
|||||||
title: function (context) {
|
title: function (context) {
|
||||||
const binStart = parseFloat(context[0].label);
|
const binStart = parseFloat(context[0].label);
|
||||||
const binEnd = binStart + binWidth;
|
const binEnd = binStart + binWidth;
|
||||||
return `$${binStart.toFixed(0)} - $${binEnd.toFixed(0)}`;
|
return `${binStart.toFixed(1)}% - ${binEnd.toFixed(1)}%`;
|
||||||
},
|
},
|
||||||
label: function (context) {
|
label: function (context) {
|
||||||
const percentage = (context.parsed.y / results.iterations * 100).toFixed(1);
|
const percentage = (context.parsed.y / results.iterations * 100).toFixed(1);
|
||||||
@@ -268,15 +277,15 @@ function drawMonteCarloHistogram(results, stats) {
|
|||||||
},
|
},
|
||||||
annotation: {
|
annotation: {
|
||||||
annotations: {
|
annotations: {
|
||||||
meanLine: {
|
medianLine: {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
xMin: ((stats.mean - min) / binWidth),
|
xMin: ((ddStats.median - min) / binWidth),
|
||||||
xMax: ((stats.mean - min) / binWidth),
|
xMax: ((ddStats.median - min) / binWidth),
|
||||||
borderColor: '#667eea',
|
borderColor: '#667eea',
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderDash: [5, 5],
|
borderDash: [5, 5],
|
||||||
label: {
|
label: {
|
||||||
content: 'Mean',
|
content: 'Median',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
position: 'top'
|
position: 'top'
|
||||||
}
|
}
|
||||||
@@ -306,7 +315,7 @@ function drawMonteCarloHistogram(results, stats) {
|
|||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Final Equity ($)',
|
text: 'Maximum Drawdown (%)',
|
||||||
color: '#a0aec0'
|
color: '#a0aec0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -21,9 +21,7 @@ class MonteCarloSimulation {
|
|||||||
* @returns {Object} Statistical results
|
* @returns {Object} Statistical results
|
||||||
*/
|
*/
|
||||||
run() {
|
run() {
|
||||||
console.log(`🎲 Running Monte Carlo simulation (${this.iterations} iterations)...`);
|
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
for (let i = 0; i < this.iterations; i++) {
|
for (let i = 0; i < this.iterations; i++) {
|
||||||
@@ -42,8 +40,6 @@ class MonteCarloSimulation {
|
|||||||
const statistics = this.calculateStatistics(results);
|
const statistics = this.calculateStatistics(results);
|
||||||
const endTime = performance.now();
|
const endTime = performance.now();
|
||||||
|
|
||||||
console.log(`✅ Monte Carlo complete in ${(endTime - startTime).toFixed(0)}ms`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
iterations: this.iterations,
|
iterations: this.iterations,
|
||||||
statistics,
|
statistics,
|
||||||
@@ -138,6 +134,9 @@ class MonteCarloSimulation {
|
|||||||
min: Math.min(...drawdowns),
|
min: Math.min(...drawdowns),
|
||||||
max: Math.max(...drawdowns),
|
max: Math.max(...drawdowns),
|
||||||
percentile5: this.getPercentile(drawdowns, 5),
|
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)
|
percentile95: this.getPercentile(drawdowns, 95)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -260,12 +259,14 @@ class MonteCarloSimulation {
|
|||||||
throw new Error('Strategy must have backtest results with trades');
|
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,
|
profit: trade.profit,
|
||||||
type: trade.type,
|
type: trade.type,
|
||||||
entry: trade.entry,
|
entry: trade.entry,
|
||||||
exit: trade.exit
|
exit: trade.exit
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return trades;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user