mirror of
https://github.com/FxPouya/FxMathQuantWebApp.git
synced 2026-07-27 18:27:44 +00:00
Add files via upload
This commit is contained in:
+171
@@ -814,4 +814,175 @@ tbody tr:hover {
|
||||
|
||||
.loading {
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Walk-Forward Analysis Badges */
|
||||
.wf-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.wf-badge-pass {
|
||||
background: rgba(72, 187, 120, 0.15);
|
||||
color: var(--accent-success);
|
||||
border: 1px solid rgba(72, 187, 120, 0.3);
|
||||
}
|
||||
|
||||
.wf-badge-fail {
|
||||
background: rgba(237, 137, 54, 0.15);
|
||||
color: var(--accent-warning);
|
||||
border: 1px solid rgba(237, 137, 54, 0.3);
|
||||
}
|
||||
|
||||
.wf-icon {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wf-text {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.wf-score {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 2px 6px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Walk-Forward Results Tab */
|
||||
.wf-summary {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
margin-bottom: 30px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.robustness-gauge {
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%);
|
||||
border-radius: 15px;
|
||||
color: white;
|
||||
min-width: 160px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.gauge-value {
|
||||
font-size: 52px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.gauge-label {
|
||||
font-size: 13px;
|
||||
opacity: 0.95;
|
||||
margin-top: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.wf-status {
|
||||
flex: 1;
|
||||
padding: 20px 25px;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.status-pass {
|
||||
background: rgba(72, 187, 120, 0.15);
|
||||
border-left: 4px solid var(--accent-success);
|
||||
color: var(--accent-success);
|
||||
}
|
||||
|
||||
.status-fail {
|
||||
background: rgba(245, 101, 101, 0.15);
|
||||
border-left: 4px solid var(--accent-danger);
|
||||
color: var(--accent-danger);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
margin: 25px 0;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comparison-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 15px 20px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--bg-card);
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.comparison-table tbody tr:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.comparison-table .good {
|
||||
color: var(--accent-success);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.comparison-table .warning {
|
||||
color: var(--accent-warning);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.comparison-table .bad {
|
||||
color: var(--accent-danger);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wf-charts {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 25px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.wf-chart-container {
|
||||
background: var(--bg-secondary);
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.wf-chart-container h4 {
|
||||
margin-bottom: 15px;
|
||||
color: var(--accent-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.wf-charts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wf-summary {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
+38
-14
@@ -211,6 +211,29 @@
|
||||
<small>Minimum trade count (10-200)</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Walk-Forward Analysis -->
|
||||
<div class="config-card">
|
||||
<h3>Walk-Forward Analysis</h3>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="enable-walkforward" checked>
|
||||
Enable Walk-Forward Validation
|
||||
</label>
|
||||
<small>Validate strategies on out-of-sample data to detect overfitting</small>
|
||||
</div>
|
||||
<div class="form-group" id="walkforward-settings">
|
||||
<label>Training Period: <span id="training-percent">70</span>%</label>
|
||||
<input type="range" id="training-ratio" min="50" max="90" value="70" step="5"
|
||||
oninput="updateTrainingSplit()">
|
||||
<div class="split-info"
|
||||
style="display: flex; justify-content: space-between; margin-top: 5px; font-size: 12px; color: #718096;">
|
||||
<span>Training: <span id="training-bars">0</span> bars</span>
|
||||
<span>Testing: <span id="testing-bars">0</span> bars</span>
|
||||
</div>
|
||||
<small>Higher ratio = more data for training, less for validation</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
@@ -297,21 +320,22 @@
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="js/utils.js"></script>
|
||||
<script src="js/strategy.js"></script>
|
||||
<script src="js/strategy.js?v=2.2"></script>
|
||||
<script src="js/backtester.js"></script>
|
||||
<script src="js/ga-engine.js"></script>
|
||||
<script src="js/rule-parser.js"></script>
|
||||
<script src="js/mq4-converter.js"></script>
|
||||
<script src="js/mq5-converter.js"></script>
|
||||
<script src="js/ctrader-converter.js"></script>
|
||||
<script src="js/pine-converter.js"></script>
|
||||
<script src="js/mq4-generator.js"></script>
|
||||
<script src="js/mq5-generator.js"></script>
|
||||
<script src="js/report-generator.js"></script>
|
||||
<script src="js/strategy-details.js"></script>
|
||||
<script src="js/html-report-generator.js"></script>
|
||||
<script src="js/ui-controller.js"></script>
|
||||
<script src="js/main.js?v=2.0"></script>
|
||||
<script src="js/walk-forward.js"></script>
|
||||
<script src="js/ga-engine.js?v=2.2"></script>
|
||||
<script src="js/rule-parser.js?v=2.1"></script>
|
||||
<script src="js/mq4-converter.js?v=2.1"></script>
|
||||
<script src="js/mq5-converter.js?v=2.1"></script>
|
||||
<script src="js/ctrader-converter.js?v=2.1"></script>
|
||||
<script src="js/pine-converter.js?v=2.1"></script>
|
||||
<script src="js/mq4-generator.js?v=2.1"></script>
|
||||
<script src="js/mq5-generator.js?v=2.1"></script>
|
||||
<script src="js/report-generator.js?v=2.1"></script>
|
||||
<script src="js/strategy-details.js?v=2.1"></script>
|
||||
<script src="js/html-report-generator.js?v=2.1"></script>
|
||||
<script src="js/ui-controller.js?v=2.1"></script>
|
||||
<script src="js/main.js?v=2.1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+36
-2
@@ -14,9 +14,15 @@ class GeneticOptimizer {
|
||||
minTrades: config.minTrades || 30,
|
||||
minPF: config.minPF || 1.5,
|
||||
maxDD: config.maxDD || 25,
|
||||
minWR: config.minWR || 45
|
||||
minWR: config.minWR || 45,
|
||||
// Walk-Forward Analysis settings
|
||||
enableWalkForward: config.enableWalkForward || false,
|
||||
trainingRatio: config.trainingRatio || 0.7
|
||||
};
|
||||
|
||||
// Debug: Log walk-forward config
|
||||
console.log('🔧 GA Config - Walk-Forward:', this.config.enableWalkForward, 'Ratio:', this.config.trainingRatio);
|
||||
|
||||
this.population = [];
|
||||
this.foundStrategies = [];
|
||||
this.isRunning = false;
|
||||
@@ -40,10 +46,38 @@ class GeneticOptimizer {
|
||||
* Evaluate fitness for all strategies
|
||||
*/
|
||||
evaluatePopulation() {
|
||||
// Log walk-forward status (only once per generation)
|
||||
if (this.currentGeneration === 0) {
|
||||
console.log('🔬 Walk-Forward Analysis:', this.config.enableWalkForward ? 'ENABLED' : 'DISABLED');
|
||||
if (this.config.enableWalkForward) {
|
||||
console.log('📊 Training Ratio:', (this.config.trainingRatio * 100) + '%');
|
||||
}
|
||||
}
|
||||
|
||||
for (const strategy of this.population) {
|
||||
// Regular backtest on full data
|
||||
const backtester = new Backtester(this.data, strategy, this.symbol);
|
||||
strategy.metrics = backtester.run();
|
||||
strategy.fitness = this.calculateFitness(strategy.metrics);
|
||||
|
||||
// Walk-forward analysis (if enabled)
|
||||
if (this.config.enableWalkForward) {
|
||||
const wfAnalysis = new WalkForwardAnalysis(
|
||||
this.data,
|
||||
this.config.trainingRatio || 0.7
|
||||
);
|
||||
strategy.walkForward = wfAnalysis.analyze(strategy, this.symbol);
|
||||
|
||||
// Penalize strategies that fail walk-forward validation
|
||||
if (!strategy.walkForward.passed) {
|
||||
strategy.fitness = this.calculateFitness(strategy.metrics) * 0.5; // 50% penalty
|
||||
} else {
|
||||
// Bonus for high robustness
|
||||
const robustnessBonus = 1 + (strategy.walkForward.robustness / 200); // Up to 50% bonus
|
||||
strategy.fitness = this.calculateFitness(strategy.metrics) * robustnessBonus;
|
||||
}
|
||||
} else {
|
||||
strategy.fitness = this.calculateFitness(strategy.metrics);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by fitness (descending)
|
||||
|
||||
+56
-1
@@ -281,7 +281,10 @@ async function startGeneration() {
|
||||
minPF: parseFloat(document.getElementById('min-pf').value),
|
||||
minWR: parseFloat(document.getElementById('min-wr').value),
|
||||
maxDD: parseFloat(document.getElementById('max-dd').value),
|
||||
minTrades: parseInt(document.getElementById('min-trades').value)
|
||||
minTrades: parseInt(document.getElementById('min-trades').value),
|
||||
// Walk-Forward Analysis settings
|
||||
enableWalkForward: document.getElementById('enable-walkforward').checked,
|
||||
trainingRatio: parseInt(document.getElementById('training-ratio').value) / 100
|
||||
};
|
||||
|
||||
// Show progress section
|
||||
@@ -469,9 +472,30 @@ function createStrategyCard(strategy, number) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'strategy-card';
|
||||
|
||||
|
||||
// Generate walk-forward badge HTML (only if walk-forward was enabled and data exists)
|
||||
let wfBadgeHTML = '';
|
||||
if (strategy.walkForward && typeof strategy.walkForward === 'object') {
|
||||
const wf = strategy.walkForward;
|
||||
if (wf.robustness !== undefined && wf.passed !== undefined) {
|
||||
const badgeClass = wf.passed ? 'wf-badge-pass' : 'wf-badge-fail';
|
||||
const badgeIcon = wf.passed ? '✓' : '⚠';
|
||||
const badgeText = wf.passed ? 'Robust' : 'Overfitted';
|
||||
wfBadgeHTML = `
|
||||
<div class="wf-badge ${badgeClass}" title="Robustness Score: ${wf.robustness}/100">
|
||||
<span class="wf-icon">${badgeIcon}</span>
|
||||
<span class="wf-text">${badgeText}</span>
|
||||
<span class="wf-score">${wf.robustness}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
const name = `Strategy #${number}`;
|
||||
card.innerHTML = `
|
||||
<div class="strategy-header">
|
||||
<div class="strategy-name">${name}</div>
|
||||
${wfBadgeHTML}
|
||||
<input type="checkbox" class="strategy-select" onchange="toggleStrategySelection(${number - 1}, this.checked)">
|
||||
</div>
|
||||
<div class="strategy-metrics">
|
||||
@@ -750,10 +774,41 @@ function formatTime(seconds) {
|
||||
return `${minutes}m ${secs}s`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update training/testing split display
|
||||
*/
|
||||
function updateTrainingSplit() {
|
||||
const ratio = parseInt(document.getElementById('training-ratio').value);
|
||||
document.getElementById('training-percent').textContent = ratio;
|
||||
|
||||
// Update bar counts if data is loaded
|
||||
if (appData.csvData) {
|
||||
const totalBars = appData.csvData.length;
|
||||
const trainingBars = Math.floor(totalBars * (ratio / 100));
|
||||
const testingBars = totalBars - trainingBars;
|
||||
|
||||
document.getElementById('training-bars').textContent = trainingBars.toLocaleString();
|
||||
document.getElementById('testing-bars').textContent = testingBars.toLocaleString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle walk-forward settings visibility
|
||||
*/
|
||||
function toggleWalkForwardSettings() {
|
||||
const enabled = document.getElementById('enable-walkforward').checked;
|
||||
const settings = document.getElementById('walkforward-settings');
|
||||
if (settings) {
|
||||
settings.style.display = enabled ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly expose functions to global scope for inline onclick handlers
|
||||
window.downloadStrategy = downloadStrategy;
|
||||
window.downloadAllStrategies = downloadAllStrategies;
|
||||
window.toggleStrategySelection = toggleStrategySelection;
|
||||
window.showComparison = showComparison;
|
||||
window.updateTrainingSplit = updateTrainingSplit;
|
||||
window.toggleWalkForwardSettings = toggleWalkForwardSettings;
|
||||
window.hideComparison = hideComparison;
|
||||
window.toggleCustomBarsInput = toggleCustomBarsInput;
|
||||
|
||||
@@ -100,6 +100,8 @@ class Strategy {
|
||||
newStrategy.closeAtOpposite = this.closeAtOpposite;
|
||||
newStrategy.metrics = this.metrics ? JSON.parse(JSON.stringify(this.metrics)) : {};
|
||||
newStrategy.fitness = this.fitness || 0;
|
||||
// Copy walk-forward analysis results
|
||||
newStrategy.walkForward = this.walkForward ? JSON.parse(JSON.stringify(this.walkForward)) : undefined;
|
||||
return newStrategy;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Walk-Forward Analysis Module
|
||||
* Validates strategies on out-of-sample data to detect overfitting
|
||||
*/
|
||||
|
||||
class WalkForwardAnalysis {
|
||||
constructor(data, trainingRatio = 0.7) {
|
||||
this.data = data;
|
||||
this.trainingRatio = trainingRatio;
|
||||
this.splitIndex = Math.floor(data.length * trainingRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split data into training and testing periods
|
||||
* @returns {Object} { training: Array, testing: Array }
|
||||
*/
|
||||
split() {
|
||||
return {
|
||||
training: this.data.slice(0, this.splitIndex),
|
||||
testing: this.data.slice(this.splitIndex),
|
||||
splitIndex: this.splitIndex,
|
||||
trainingBars: this.splitIndex,
|
||||
testingBars: this.data.length - this.splitIndex
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Run walk-forward analysis on a strategy
|
||||
* @param {Object} strategy - Strategy to analyze
|
||||
* @param {string} symbol - Trading symbol
|
||||
* @returns {Object} Analysis results
|
||||
*/
|
||||
analyze(strategy, symbol = '') {
|
||||
const { training, testing } = this.split();
|
||||
|
||||
// Backtest on training data
|
||||
const trainingBacktest = new Backtester(training, strategy, symbol);
|
||||
const trainingResults = trainingBacktest.run();
|
||||
|
||||
// Backtest on testing data
|
||||
const testingBacktest = new Backtester(testing, strategy, symbol);
|
||||
const testingResults = testingBacktest.run();
|
||||
|
||||
// Calculate degradation
|
||||
const degradation = this.calculateDegradation(trainingResults, testingResults);
|
||||
|
||||
// Calculate robustness score
|
||||
const robustnessScore = this.calculateRobustness(degradation, trainingResults, testingResults);
|
||||
|
||||
// Determine if strategy passed
|
||||
const passed = robustnessScore >= 60;
|
||||
|
||||
return {
|
||||
training: trainingResults,
|
||||
testing: testingResults,
|
||||
degradation: degradation,
|
||||
robustness: robustnessScore,
|
||||
passed: passed,
|
||||
splitInfo: {
|
||||
trainingBars: training.length,
|
||||
testingBars: testing.length,
|
||||
trainingRatio: this.trainingRatio
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate performance degradation between training and testing
|
||||
* @param {Object} train - Training results
|
||||
* @param {Object} test - Testing results
|
||||
* @returns {Object} Degradation metrics
|
||||
*/
|
||||
calculateDegradation(train, test) {
|
||||
return {
|
||||
profitFactor: this.calcPercentDegradation(train.profitFactor, test.profitFactor),
|
||||
winRate: train.winRate - test.winRate,
|
||||
maxDrawdown: test.maxDrawdown - train.maxDrawdown,
|
||||
netProfit: this.calcPercentDegradation(train.netProfit, test.netProfit),
|
||||
totalTrades: test.totalTrades,
|
||||
avgWin: this.calcPercentDegradation(train.avgWin, test.avgWin),
|
||||
avgLoss: this.calcPercentDegradation(train.avgLoss, test.avgLoss)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate percentage degradation
|
||||
* @param {number} trainValue - Training value
|
||||
* @param {number} testValue - Testing value
|
||||
* @returns {number} Degradation percentage
|
||||
*/
|
||||
calcPercentDegradation(trainValue, testValue) {
|
||||
if (trainValue === 0) return 0;
|
||||
return ((trainValue - testValue) / trainValue * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate robustness score (0-100)
|
||||
* Higher score = more robust strategy
|
||||
* @param {Object} degradation - Degradation metrics
|
||||
* @param {Object} train - Training results
|
||||
* @param {Object} test - Testing results
|
||||
* @returns {number} Robustness score
|
||||
*/
|
||||
calculateRobustness(degradation, train, test) {
|
||||
// Profit Factor score (40% weight)
|
||||
// Lower degradation = higher score
|
||||
const pfDeg = Math.abs(degradation.profitFactor);
|
||||
let pfScore = 100;
|
||||
if (pfDeg > 50) pfScore = 0;
|
||||
else if (pfDeg > 30) pfScore = 30;
|
||||
else if (pfDeg > 15) pfScore = 60;
|
||||
else pfScore = 100;
|
||||
|
||||
// Win Rate score (30% weight)
|
||||
const wrDeg = Math.abs(degradation.winRate);
|
||||
let wrScore = 100;
|
||||
if (wrDeg > 20) wrScore = 0;
|
||||
else if (wrDeg > 10) wrScore = 40;
|
||||
else if (wrDeg > 5) wrScore = 70;
|
||||
else wrScore = 100;
|
||||
|
||||
// Drawdown score (30% weight)
|
||||
// For drawdown, increase is bad
|
||||
const ddDeg = degradation.maxDrawdown;
|
||||
let ddScore = 100;
|
||||
if (ddDeg > 15) ddScore = 0;
|
||||
else if (ddDeg > 10) ddScore = 40;
|
||||
else if (ddDeg > 5) ddScore = 70;
|
||||
else if (ddDeg < -5) ddScore = 100; // Better drawdown in testing
|
||||
else ddScore = 85;
|
||||
|
||||
// Additional checks
|
||||
// Penalize if testing has too few trades
|
||||
let tradesPenalty = 0;
|
||||
if (test.totalTrades < 20) {
|
||||
tradesPenalty = 20;
|
||||
} else if (test.totalTrades < 30) {
|
||||
tradesPenalty = 10;
|
||||
}
|
||||
|
||||
// Bonus if testing performs better
|
||||
let bonus = 0;
|
||||
if (test.profitFactor > train.profitFactor && test.winRate >= train.winRate) {
|
||||
bonus = 10;
|
||||
}
|
||||
|
||||
// Weighted average
|
||||
const score = (pfScore * 0.4) + (wrScore * 0.3) + (ddScore * 0.3) - tradesPenalty + bonus;
|
||||
|
||||
return Math.max(0, Math.min(100, Math.round(score)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get degradation severity level
|
||||
* @param {number} value - Degradation value
|
||||
* @param {boolean} inverse - If true, higher is worse (for drawdown)
|
||||
* @returns {string} 'good', 'warning', or 'bad'
|
||||
*/
|
||||
getDegradationLevel(value, inverse = false) {
|
||||
const absValue = Math.abs(value);
|
||||
|
||||
if (inverse) {
|
||||
// For drawdown increase
|
||||
if (value < 0) return 'good'; // Improved
|
||||
if (absValue < 5) return 'good';
|
||||
if (absValue < 10) return 'warning';
|
||||
return 'bad';
|
||||
} else {
|
||||
// For profit factor, win rate degradation
|
||||
if (absValue < 10) return 'good';
|
||||
if (absValue < 20) return 'warning';
|
||||
return 'bad';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate summary text for walk-forward results
|
||||
* @param {Object} results - Walk-forward results
|
||||
* @returns {string} Summary text
|
||||
*/
|
||||
generateSummary(results) {
|
||||
const { robustness, passed, degradation } = results;
|
||||
|
||||
if (passed) {
|
||||
if (robustness >= 80) {
|
||||
return 'Excellent! Strategy shows strong robustness with minimal overfitting.';
|
||||
} else if (robustness >= 70) {
|
||||
return 'Good! Strategy performs well on out-of-sample data.';
|
||||
} else {
|
||||
return 'Acceptable. Strategy shows reasonable robustness but monitor performance.';
|
||||
}
|
||||
} else {
|
||||
if (robustness < 40) {
|
||||
return 'Warning! High overfitting risk. Strategy may not perform well in live trading.';
|
||||
} else {
|
||||
return 'Caution. Strategy shows some overfitting. Consider re-optimization or more data.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in other modules
|
||||
window.WalkForwardAnalysis = WalkForwardAnalysis;
|
||||
+97
@@ -191,6 +191,7 @@
|
||||
<li><a href="#export">Exporting Data from MT4/MT5</a></li>
|
||||
<li><a href="#upload">Uploading Data</a></li>
|
||||
<li><a href="#config">Configuring Strategy Generation</a></li>
|
||||
<li><a href="#walkforward">Walk-Forward Analysis</a></li>
|
||||
<li><a href="#generate">Generating Strategies</a></li>
|
||||
<li><a href="#results">Viewing Results</a></li>
|
||||
<li><a href="#download">Downloading Strategies</a></li>
|
||||
@@ -338,6 +339,102 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 id="walkforward">🔬 Walk-Forward Analysis</h3>
|
||||
<p>Walk-forward analysis validates strategies on out-of-sample data to detect overfitting and ensure real-world
|
||||
performance.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>What is Overfitting?</strong> A strategy that performs well on historical data but fails in live
|
||||
trading due to being too closely fitted to past price patterns.
|
||||
</div>
|
||||
|
||||
<h4>How It Works</h4>
|
||||
<ol>
|
||||
<li><strong>Data Splitting:</strong> Historical data is divided into training and testing periods</li>
|
||||
<li><strong>Training Period:</strong> Strategy is optimized on this data (e.g., 70% of bars)</li>
|
||||
<li><strong>Testing Period:</strong> Strategy is validated on unseen data (e.g., 30% of bars)</li>
|
||||
<li><strong>Performance Comparison:</strong> Metrics are compared between training and testing</li>
|
||||
<li><strong>Robustness Score:</strong> A score (0-100) indicates how well the strategy generalizes</li>
|
||||
</ol>
|
||||
|
||||
<h4>Configuration</h4>
|
||||
<ul>
|
||||
<li><strong>Enable Walk-Forward Validation:</strong> Check this box to activate the feature</li>
|
||||
<li><strong>Training Period:</strong> Adjust the slider (50-90%, default: 70%)
|
||||
<ul>
|
||||
<li>Higher ratio = More data for training, less for validation</li>
|
||||
<li>Lower ratio = Less training data, more for validation</li>
|
||||
<li>Recommended: 70% for most cases</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Understanding the Badges</h4>
|
||||
<p>Each strategy card displays a walk-forward badge showing its robustness:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Badge</th>
|
||||
<th>Score Range</th>
|
||||
<th>Meaning</th>
|
||||
<th>Recommendation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>✓ ROBUST (Green)</td>
|
||||
<td>60-100</td>
|
||||
<td>Strategy passed validation</td>
|
||||
<td>Safe to use for live trading</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>⚠ OVERFITTED (Orange)</td>
|
||||
<td>0-59</td>
|
||||
<td>Strategy failed validation</td>
|
||||
<td>Avoid - likely curve-fitted</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>Robustness Score Interpretation</h4>
|
||||
<ul>
|
||||
<li><strong>80-100:</strong> Excellent - Minimal degradation, high confidence</li>
|
||||
<li><strong>60-79:</strong> Good - Acceptable degradation, suitable for trading</li>
|
||||
<li><strong>40-59:</strong> Moderate - Concerning degradation, needs review</li>
|
||||
<li><strong>0-39:</strong> Poor - High overfitting risk, avoid</li>
|
||||
</ul>
|
||||
|
||||
<h4>What Gets Measured</h4>
|
||||
<p>The robustness score is calculated based on degradation in:</p>
|
||||
<ul>
|
||||
<li><strong>Profit Factor:</strong> How much profit factor drops in testing vs. training</li>
|
||||
<li><strong>Win Rate:</strong> Percentage point decrease in win rate</li>
|
||||
<li><strong>Max Drawdown:</strong> Increase in maximum drawdown</li>
|
||||
<li><strong>Trade Count:</strong> Sufficient trades in testing period</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Important:</strong> A strategy with excellent training performance but poor testing performance is
|
||||
likely overfitted and should be avoided.
|
||||
</div>
|
||||
|
||||
<h4>Best Practices</h4>
|
||||
<ul>
|
||||
<li>Use at least 1,000 bars of data (minimum 300 for testing)</li>
|
||||
<li>Keep training ratio at 70% for balanced results</li>
|
||||
<li>Only trade strategies with robustness score ≥ 60</li>
|
||||
<li>Prefer strategies with scores above 80 for higher confidence</li>
|
||||
<li>If all strategies fail, try different data or settings</li>
|
||||
</ul>
|
||||
|
||||
<h4>Impact on Strategy Generation</h4>
|
||||
<p>When walk-forward is enabled:</p>
|
||||
<ul>
|
||||
<li><strong>Failed strategies:</strong> Receive 50% fitness penalty (less likely to be selected)</li>
|
||||
<li><strong>Robust strategies:</strong> Receive up to 50% fitness bonus (prioritized)</li>
|
||||
<li><strong>Result:</strong> GA naturally evolves toward robust, non-overfitted strategies</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="generate">🎯 Generating Strategies</h2>
|
||||
<ol>
|
||||
<li>Review your settings</li>
|
||||
|
||||
Reference in New Issue
Block a user