Files
FxMathQuantWebApp/js/mq5-generator.js
T
2025-12-24 13:07:51 +03:30

203 lines
6.5 KiB
JavaScript

/**
* MQ5 Code Generator - Generates MetaTrader 5 Expert Advisor code
*/
class MQ5Generator {
constructor(strategy, strategyName) {
this.strategy = strategy;
this.name = strategyName || this.generateName();
}
generateName() {
const pf = this.strategy.metrics.profitFactor.toFixed(2).replace('.', '_');
const wr = Math.round(this.strategy.metrics.winRate);
const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
return `FxMath_PF${pf}_WR${wr}_${timestamp}`;
}
generate() {
return `//+------------------------------------------------------------------+
//| ${this.name}.mq5 |
//| Generated by FxMathQuant Web |
//| https://fxmathquant.com |
//+------------------------------------------------------------------+
#property copyright "FxMathQuant"
#property link "https://fxmathquant.com"
#property version "1.00"
//--- Include libraries
#include <Trade\\Trade.mqh>
//--- Input Parameters
input double LotSize = 0.01; // Lot size
input int MagicNumber = ${Math.floor(Math.random() * 9000) + 1000}; // Magic number
input int ATR_Period = ${this.strategy.atrPeriod}; // ATR period
input double SL_Multiplier = ${this.strategy.slMultiplier.toFixed(2)}; // Stop Loss multiplier
input double TP_Multiplier = ${this.strategy.tpMultiplier.toFixed(2)}; // Take Profit multiplier
input int Slippage = 3; // Slippage in points
//--- Global Variables
CTrade trade;
int atrHandle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set magic number
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
// Create ATR indicator handle
atrHandle = iATR(_Symbol, _Period, ATR_Period);
if(atrHandle == INVALID_HANDLE)
{
Print("Error creating ATR indicator");
return(INIT_FAILED);
}
Print("${this.name} initialized");
Print("Strategy Performance: PF=${this.strategy.metrics.profitFactor.toFixed(2)}, WR=${this.strategy.metrics.winRate.toFixed(1)}%");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release indicator handle
if(atrHandle != INVALID_HANDLE)
IndicatorRelease(atrHandle);
Print("${this.name} stopped");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if we already have an open position
if(PositionSelect(_Symbol))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
return; // Position already open
}
// Get ATR value
double atrBuffer[];
ArraySetAsSeries(atrBuffer, true);
if(CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0)
return;
double atr = atrBuffer[0];
if(atr == 0) return;
// Check for BUY signal
if(CheckBuySignal())
{
double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = entry - (atr * SL_Multiplier);
double tp = entry + (atr * TP_Multiplier);
// Normalize prices
sl = NormalizeDouble(sl, _Digits);
tp = NormalizeDouble(tp, _Digits);
// Open BUY position
if(trade.Buy(LotSize, _Symbol, entry, sl, tp, "${this.name}"))
Print("BUY position opened @ ", entry, " SL:", sl, " TP:", tp);
else
Print("Error opening BUY position: ", trade.ResultRetcode());
}
}
//+------------------------------------------------------------------+
//| Check BUY signal based on strategy rules |
//+------------------------------------------------------------------+
bool CheckBuySignal()
{
${this.generateRulesCode()}
return true; // All rules passed
}
${this.generateHelperFunctions()}
//+------------------------------------------------------------------+
`;
}
generateRulesCode() {
return this.strategy.rules.map((rule, index) => {
const condition = this.ruleToMQ5(rule);
return ` // Rule ${index + 1}
if(!(${condition})) return false;`;
}).join('\n');
}
ruleToMQ5(rule) {
if (rule.type === 'simple') {
const left = this.priceToMQ5(rule.left.price, rule.left.shift);
const right = this.priceToMQ5(rule.right.price, rule.right.shift);
return `${left} ${rule.operator} ${right}`;
} else {
// Arithmetic rule
const left1 = this.priceToMQ5(rule.left.price1, rule.left.shift1);
const left2 = this.priceToMQ5(rule.left.price2, rule.left.shift2);
const right = this.priceToMQ5(rule.right.price, rule.right.shift);
return `(${left1} ${rule.left.op} ${left2}) ${rule.operator} (${right} * ${rule.right.multiplier})`;
}
}
priceToMQ5(priceType, shift) {
const type = priceType.charAt(0).toUpperCase() + priceType.slice(1).toLowerCase();
return `iClose(_Symbol, _Period, ${shift})`.replace('Close', type);
}
generateHelperFunctions() {
return `//+------------------------------------------------------------------+
//| Get price value at specific shift |
//+------------------------------------------------------------------+
double iOpen(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
{
double buffer[];
ArraySetAsSeries(buffer, true);
if(CopyOpen(symbol, timeframe, shift, 1, buffer) > 0)
return buffer[0];
return 0;
}
double iHigh(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
{
double buffer[];
ArraySetAsSeries(buffer, true);
if(CopyHigh(symbol, timeframe, shift, 1, buffer) > 0)
return buffer[0];
return 0;
}
double iLow(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
{
double buffer[];
ArraySetAsSeries(buffer, true);
if(CopyLow(symbol, timeframe, shift, 1, buffer) > 0)
return buffer[0];
return 0;
}
double iClose(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
{
double buffer[];
ArraySetAsSeries(buffer, true);
if(CopyClose(symbol, timeframe, shift, 1, buffer) > 0)
return buffer[0];
return 0;
}
`;
}
}