Add files via upload

This commit is contained in:
FxPouya
2025-12-24 20:14:48 +03:30
committed by GitHub
parent 477ab2baef
commit f187b69173
6 changed files with 133 additions and 18 deletions
+8 -1
View File
@@ -117,13 +117,19 @@
</div>
<div class="form-group" style="margin-top: 20px;">
<label>Data Size Limit (bars)</label>
<select id="data-size-limit" class="form-select">
<select id="data-size-limit" class="form-select" onchange="toggleCustomBarsInput()">
<option value="0">Use All Data</option>
<option value="500">Last 500 bars</option>
<option value="1000">Last 1,000 bars</option>
<option value="2000">Last 2,000 bars</option>
<option value="3000">Last 3,000 bars</option>
<option value="5000" selected>Last 5,000 bars (Recommended)</option>
<option value="10000">Last 10,000 bars</option>
<option value="custom">Custom...</option>
</select>
<input type="number" id="custom-bars-input" class="form-select"
placeholder="Enter custom number of bars" min="100" step="100"
style="margin-top: 10px; display: none;">
<small>Limit data size for faster processing. Recommended: 5,000 bars</small>
</div>
<button class="btn-success" id="continue-btn">Continue to Configuration →</button>
@@ -290,6 +296,7 @@
</footer>
<!-- Scripts -->
<script src="js/utils.js"></script>
<script src="js/strategy.js"></script>
<script src="js/backtester.js"></script>
<script src="js/ga-engine.js"></script>
+14 -8
View File
@@ -2,9 +2,15 @@
* Backtester - Fast backtesting engine using typed arrays
*/
class Backtester {
constructor(data, strategy) {
constructor(data, strategy, symbol = '') {
this.strategy = strategy;
this.dataLength = data.length;
this.symbol = symbol;
// Get profit multiplier based on symbol type
this.profitMultiplier = typeof getProfitMultiplier === 'function'
? getProfitMultiplier(symbol)
: 10000; // Default to standard forex
// Use typed arrays for performance
this.open = new Float64Array(data.length);
@@ -43,7 +49,7 @@ class Backtester {
if (position.type === 'BUY' && this.checkSellSignal(i)) {
// Close BUY and open SELL
const closePrice = this.close[i];
const profit = closePrice - position.entry;
const profit = (closePrice - position.entry) * this.profitMultiplier;
balance += profit;
trades.push({
type: 'BUY',
@@ -60,7 +66,7 @@ class Backtester {
} else if (position.type === 'SELL' && this.checkBuySignal(i)) {
// Close SELL and open BUY
const closePrice = this.close[i];
const profit = position.entry - closePrice;
const profit = (position.entry - closePrice) * this.profitMultiplier;
balance += profit;
trades.push({
type: 'SELL',
@@ -101,7 +107,7 @@ class Backtester {
// Close any open position at the end
if (position) {
const exitPrice = this.close[this.dataLength - 1];
const profit = exitPrice - position.entry;
const profit = (exitPrice - position.entry) * this.profitMultiplier;
balance += profit;
trades.push({
type: 'BUY',
@@ -266,7 +272,7 @@ class Backtester {
type: position.type,
entry: position.entry,
exit: position.tp,
profit: position.tp - position.entry,
profit: (position.tp - position.entry) * this.profitMultiplier,
reason: 'take_profit',
openTime: position.openTime,
closeTime: this.time[index]
@@ -278,7 +284,7 @@ class Backtester {
type: position.type,
entry: position.entry,
exit: position.sl,
profit: position.sl - position.entry,
profit: (position.sl - position.entry) * this.profitMultiplier,
reason: 'stop_loss',
openTime: position.openTime,
closeTime: this.time[index]
@@ -291,7 +297,7 @@ class Backtester {
type: position.type,
entry: position.entry,
exit: position.tp,
profit: position.entry - position.tp,
profit: (position.entry - position.tp) * this.profitMultiplier,
reason: 'take_profit',
openTime: position.openTime,
closeTime: this.time[index]
@@ -303,7 +309,7 @@ class Backtester {
type: position.type,
entry: position.entry,
exit: position.sl,
profit: position.entry - position.sl,
profit: (position.entry - position.sl) * this.profitMultiplier,
reason: 'stop_loss',
openTime: position.openTime,
closeTime: this.time[index]
+3 -2
View File
@@ -2,8 +2,9 @@
* Genetic Algorithm Engine - Evolves trading strategies
*/
class GeneticOptimizer {
constructor(data, config) {
constructor(data, config, symbol = '') {
this.data = data;
this.symbol = symbol;
this.config = {
populationSize: config.populationSize || 100,
generations: config.generations || 50,
@@ -40,7 +41,7 @@ class GeneticOptimizer {
*/
evaluatePopulation() {
for (const strategy of this.population) {
const backtester = new Backtester(this.data, strategy);
const backtester = new Backtester(this.data, strategy, this.symbol);
strategy.metrics = backtester.run();
strategy.fitness = this.calculateFitness(strategy.metrics);
}
+5 -3
View File
@@ -3,10 +3,12 @@
* Generates comprehensive HTML reports with full trading statement
*/
function generateHTMLReport(strategy, index) {
function generateHTMLReport(strategy, index, symbol = '') {
const m = strategy.metrics;
const name = `FxMath_${String(index + 1).padStart(3, '0')}_PF${(m.profitFactor || 0).toFixed(2).replace('.', '_')}_WR${Math.round(m.winRate || 0)}`;
// Note: Profits are already multiplied in the backtester, so we can display them directly
// Generate SELL rules (inverted operators)
const sellRulesHTML = strategy.rules.map((rule, i) => {
const invertedOp = rule.operator === '>' ? '<=' :
@@ -44,8 +46,8 @@ function generateHTMLReport(strategy, index) {
<td><span class="badge ${typeClass}">${trade.type}</span></td>
<td>${new Date(trade.openTime).toLocaleString()}</td>
<td>${new Date(trade.closeTime).toLocaleString()}</td>
<td>$${trade.entry.toFixed(2)}</td>
<td>$${trade.exit.toFixed(2)}</td>
<td>$${trade.entry.toFixed(5)}</td>
<td>$${trade.exit.toFixed(5)}</td>
<td class="${profitClass}">$${trade.profit.toFixed(2)}</td>
<td>${trade.reason.toUpperCase()}</td>
</tr>
+38 -4
View File
@@ -6,6 +6,7 @@
let appData = {
csvData: null,
dataName: '',
symbol: '',
optimizer: null,
foundStrategies: [],
selectedStrategies: new Set(),
@@ -139,7 +140,14 @@ function handleFileUpload(file) {
}));
appData.dataName = file.name;
// Extract symbol from filename
appData.symbol = typeof extractSymbolFromFilename === 'function'
? extractSymbolFromFilename(file.name)
: '';
console.log('✅ Data loaded successfully:', appData.csvData.length, 'bars');
console.log('📊 Detected symbol:', appData.symbol || 'Unknown');
// Apply data size limit
applyDataSizeLimit();
@@ -189,13 +197,37 @@ function displayDataPreview() {
* Apply data size limit
*/
function applyDataSizeLimit() {
const limit = parseInt(document.getElementById('data-size-limit').value);
const limitSelect = document.getElementById('data-size-limit');
const customInput = document.getElementById('custom-bars-input');
let limit;
if (limitSelect.value === 'custom') {
limit = parseInt(customInput.value) || 0;
} else {
limit = parseInt(limitSelect.value);
}
if (limit > 0 && appData.csvData.length > limit) {
console.log(`✂️ Limiting data from ${appData.csvData.length} to ${limit} bars`);
appData.csvData = appData.csvData.slice(-limit); // Take last N bars
}
}
/**
* Toggle custom bars input visibility
*/
function toggleCustomBarsInput() {
const limitSelect = document.getElementById('data-size-limit');
const customInput = document.getElementById('custom-bars-input');
if (limitSelect.value === 'custom') {
customInput.style.display = 'block';
customInput.focus();
} else {
customInput.style.display = 'none';
}
}
/**
* Detect timeframe from data
*/
@@ -269,7 +301,8 @@ async function startGeneration() {
// Create optimizer
console.log('🧬 Creating GA optimizer with config:', config);
appData.optimizer = new GeneticOptimizer(appData.csvData, config);
console.log('📊 Using symbol:', appData.symbol || 'Unknown');
appData.optimizer = new GeneticOptimizer(appData.csvData, config, appData.symbol);
// Start time
const startTime = Date.now();
@@ -520,8 +553,8 @@ function downloadStrategy(index, type) {
content = converter.generate();
filename = name + '.pine';
} else if (type === 'report') {
// Use new HTML report generator
content = generateHTMLReport(strategy, index);
// Use new HTML report generator with symbol
content = generateHTMLReport(strategy, index, appData.symbol);
filename = name + '_report.html';
} else if (type === 'json') {
content = JSON.stringify(strategy.toJSON(), null, 2);
@@ -723,3 +756,4 @@ window.downloadAllStrategies = downloadAllStrategies;
window.toggleStrategySelection = toggleStrategySelection;
window.showComparison = showComparison;
window.hideComparison = hideComparison;
window.toggleCustomBarsInput = toggleCustomBarsInput;
+65
View File
@@ -0,0 +1,65 @@
/**
* Utility Functions for FxMath Quant
* Helper functions for profit calculations and formatting
*/
/**
* Get profit multiplier based on symbol type
* @param {string} symbol - Trading symbol (e.g., "EURUSD", "USDJPY", "XAUUSD")
* @returns {number} - Multiplier to convert price difference to pips/points
*/
function getProfitMultiplier(symbol) {
if (!symbol) return 10000; // Default to standard forex
const upperSymbol = symbol.toUpperCase();
// Check for JPY pairs (2 decimal places)
if (upperSymbol.includes('JPY')) {
return 100;
}
// Check for XAU (Gold) - already in dollars
if (upperSymbol.includes('XAU')) {
return 1;
}
// Default: standard forex pairs (EURUSD, GBPUSD, etc.) - 4 decimal places
return 10000;
}
/**
* Format profit value with appropriate multiplier
* @param {number} profit - Raw profit value (price difference)
* @param {string} symbol - Trading symbol
* @param {number} decimals - Number of decimal places (default: 2)
* @returns {string} - Formatted profit string
*/
function formatProfit(profit, symbol, decimals = 2) {
const multiplier = getProfitMultiplier(symbol);
return (profit * multiplier).toFixed(decimals);
}
/**
* Extract symbol from filename
* @param {string} filename - CSV filename (e.g., "EURUSD_H1.csv")
* @returns {string} - Symbol name
*/
function extractSymbolFromFilename(filename) {
if (!filename) return '';
// Remove path and extension
const basename = filename.split('/').pop().split('\\').pop();
const nameWithoutExt = basename.replace(/\.(csv|txt)$/i, '');
// Extract symbol (everything before first underscore or period)
const symbol = nameWithoutExt.split(/[_\.]/)[0];
return symbol || '';
}
// Export functions
if (typeof window !== 'undefined') {
window.getProfitMultiplier = getProfitMultiplier;
window.formatProfit = formatProfit;
window.extractSymbolFromFilename = extractSymbolFromFilename;
}