/** * 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 //--- 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; } `; } }