diff --git a/index.html b/index.html
index 14cdd9b..216c7d2 100644
--- a/index.html
+++ b/index.html
@@ -117,13 +117,19 @@
-
@@ -290,6 +296,7 @@
+
diff --git a/js/backtester.js b/js/backtester.js
index 03b1fac..55ff2d4 100644
--- a/js/backtester.js
+++ b/js/backtester.js
@@ -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]
diff --git a/js/ga-engine.js b/js/ga-engine.js
index aef4428..6aa767d 100644
--- a/js/ga-engine.js
+++ b/js/ga-engine.js
@@ -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);
}
diff --git a/js/html-report-generator.js b/js/html-report-generator.js
index 3ac57b2..fd6601b 100644
--- a/js/html-report-generator.js
+++ b/js/html-report-generator.js
@@ -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) {
${trade.type} |
${new Date(trade.openTime).toLocaleString()} |
${new Date(trade.closeTime).toLocaleString()} |
- $${trade.entry.toFixed(2)} |
- $${trade.exit.toFixed(2)} |
+ $${trade.entry.toFixed(5)} |
+ $${trade.exit.toFixed(5)} |
$${trade.profit.toFixed(2)} |
${trade.reason.toUpperCase()} |
diff --git a/js/main.js b/js/main.js
index e6b858b..dde4fb1 100644
--- a/js/main.js
+++ b/js/main.js
@@ -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;
diff --git a/js/utils.js b/js/utils.js
new file mode 100644
index 0000000..787527d
--- /dev/null
+++ b/js/utils.js
@@ -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;
+}