//+------------------------------------------------------------------+ //| NeuralNetEA.mq5 | //| Copyright 2025, Google Gemini | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Google Gemini" #property link "https://www.google.com" #property version "3.00" #include #include input string InpModelFile = "PriceActionNN_v3.bin"; input double InpConfidence = 0.8; input double InpLotSize = 0.1; input int InpMagic = 123456; // --- Model Parameters (Loaded from File) --- int gLookback; int gAsianStart, gAsianEnd; int gLondonStart, gLondonEnd; int gNYStart, gNYEnd; double gTargetTP_Mult, gTargetSL_Mult; CTrade Trade; CNeuralNet *Net; int GlobalTopology[]; datetime lastBarTime = 0; int handleEMA50, handleEMA200, handleATR_D1; // Forward Decl bool LoadModelWithHeader(string filename); int OnInit() { Trade.SetExpertMagicNumber(InpMagic); handleEMA50 = iMA(_Symbol, _Period, 50, 0, MODE_EMA, PRICE_CLOSE); handleEMA200 = iMA(_Symbol, _Period, 200, 0, MODE_EMA, PRICE_CLOSE); handleATR_D1 = iATR(_Symbol, PERIOD_D1, 14); if(handleEMA50 == INVALID_HANDLE || handleEMA200 == INVALID_HANDLE || handleATR_D1 == INVALID_HANDLE) return(INIT_FAILED); if(!LoadModelWithHeader(InpModelFile)) { Print("Fatal Error: Could not load model and metadata."); return(INIT_FAILED); } Print("Model Loaded Successfully."); PrintFormat("Config: Lookback=%d, Risk=%.1f/%.1f ATR", gLookback, gTargetSL_Mult, gTargetTP_Mult); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { if(CheckPointer(Net) != POINTER_INVALID) delete Net; IndicatorRelease(handleEMA50); IndicatorRelease(handleEMA200); IndicatorRelease(handleATR_D1); } // Optimized GetSession: Only scans necessary period void GetSession(MqlRates &rates[], int currentIdx, int startHour, int endHour, double &H, double &L) { // Need Past Finished Session. // currentIdx is index in 'rates'. rates[0] is newest. // If current Hour >= endHour, we scan today (until index reaches endHour). // If current Hour < endHour, we scan Yesterday. // Simplification for EA: // Just scan 24h back from currentIdx, and verify Hour check. H = -1; L = 999999; bool found = false; datetime t = rates[currentIdx].time; MqlDateTime dt; TimeToStruct(t, dt); int curH = dt.hour; int scanStart = 0; // bars back from currentIdx // Logic: // If currently In Session or Before Session -> We need YESTERDAY's session. // If currently After Session -> We need TODAY's session. bool lookForYesterday = (curH < endHour); // We scan back up to 2880 bars (2 days) to be safe for(int k=1; k<2000; k++) { int idx = currentIdx + k; if(idx >= ArraySize(rates)) break; datetime subT = rates[idx].time; MqlDateTime subDt; TimeToStruct(subT, subDt); int h = subDt.hour; // Check if this bar belongs to the Target Session if(h >= startHour && h < endHour) { // Found a bar in session. // Is it the RIGHT day? // If we lookForYesterday, day_of_year must be != dt.day_of_year // (Edge case: New Year) bool isSameDay = (subDt.day_of_year == dt.day_of_year && subDt.year == dt.year); if(lookForYesterday && isSameDay) continue; // Skip today's bars // If we are here, it's valid. if(rates[idx].high > H || H == -1) H = rates[idx].high; if(rates[idx].low < L) L = rates[idx].low; found = true; } else { // If we were inside the session loop and now we are OUT (h < startHour), we are done? // Careful with day boundaries. if(found) { // We found session bars, now we exited the session window. It's fully scanned. break; } } } if(H == -1) { H=rates[currentIdx].high; L=rates[currentIdx].low; } // Fallback } void OnTick() { datetime time = iTime(_Symbol, _Period, 0); if(time == lastBarTime) return; lastBarTime = time; int count = gLookback + 2000; MqlRates rates[]; ArraySetAsSeries(rates, true); double ema50[], ema200[]; ArraySetAsSeries(ema50, true); ArraySetAsSeries(ema200, true); if(CopyRates(_Symbol, _Period, 0, count, rates) < count || CopyBuffer(handleEMA50, 0, 0, gLookback+1, ema50) < gLookback || CopyBuffer(handleEMA200, 0, 0, gLookback+1, ema200) < gLookback) return; // Pre-calc sessions for Current Time (Index 0) int featuresPerBar = 17; int inputSize = gLookback * featuresPerBar; double inputs[]; ArrayResize(inputs, inputSize); // Cache to avoid rescanning if we are in same context? double prevAH=-1.0; double prevAL=0.0, prevLH=0.0, prevLL=0.0, prevNH=0.0, prevNL=0.0; int prevHour = -1; for(int k=0; k atrArray[1]) ? atrArray[0] : atrArray[1]; double distTP = atrToUse * gTargetTP_Mult; double distSL = atrToUse * gTargetSL_Mult; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); Print("Signal: ", signal, " | ATR: ", DoubleToString(atrToUse, _Digits), " | TP: ", DoubleToString(distTP, _Digits), " SL: ", DoubleToString(distSL, _Digits)); if(signal > InpConfidence && !PositionSelect(_Symbol)) { Trade.Buy(InpLotSize, _Symbol, 0, ask - distSL, ask + distTP, "NN Buy"); } else if(signal < -InpConfidence && !PositionSelect(_Symbol)) { Trade.Sell(InpLotSize, _Symbol, 0, bid + distSL, bid - distTP, "NN Sell"); } } // Load Model with Header bool LoadModelWithHeader(string filename) { int handle = FileOpen(filename, FILE_READ|FILE_BIN|FILE_COMMON); if(handle == INVALID_HANDLE) { Print("Error opening model file."); return false; } int version = FileReadInteger(handle); if(version != 2) { Print("Error: Model version match (Expected 2)"); FileClose(handle); return false; } gLookback = FileReadInteger(handle); int topologyCnt = FileReadInteger(handle); ArrayResize(GlobalTopology, topologyCnt); for(int i=0; i