//+------------------------------------------------------------------+ //| AdvancedPriceActionEA.mq5 | //| Copyright 2025, Google Gemini | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Google Gemini" #property link "https://www.google.com" #property version "1.00" #include #include input string InpModelFile = "AdvancedPriceActionNN.bin"; input double InpConfidence = 0.8; input double InpLotSize = 0.1; input int InpMagic = 1234567; // --- 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; // --- Structures --- struct SessionStats { double H, L; double Volume; // Total volume double OR30_H, OR30_L; double OR30_Vol; bool valid; }; struct DailyProfile { double POC; double VAH, VAL; double Crests[]; double Troughs[]; bool valid; }; struct SessionData { SessionStats Asian; SessionStats London; SessionStats NY; DailyProfile Profile; }; // Maps DayIndex -> Data SessionData DayMap[]; datetime baseTime; // VWAP Arrays struct VWAPPoint { double vwap_d, std_d; double vwap_w, std_w; double vwap_m, std_m; }; VWAPPoint VWAPBuffer[]; double AvgVol_Asian[2], AvgVol_London[2], AvgVol_NY[2]; // --- Helper Functions --- int GetDayIndex(datetime t) { return (int)((t - baseTime) / 86400); } double MathMean(double &arr[]) { if(ArraySize(arr)==0) return 1; double s=0; for(int i=0; i= end) { out.valid=false; return; } double minP = 999999, maxP = -1; double totalVol = 0; for(int i=start; i maxP) maxP = rates[i].high; totalVol += (double)rates[i].tick_volume; } if(maxP <= minP) { out.valid=false; return; } double point = _Point; if(point == 0) point = 0.0001; int steps = (int)((maxP - minP) / point) + 1; if(steps > 5000) steps = 5000; double stepSize = (maxP - minP) / steps; if(stepSize == 0) stepSize = point; double hist[]; ArrayResize(hist, steps); ArrayInitialize(hist, 0); for(int i=start; i= 0 && idx < steps) hist[idx] += (double)rates[i].tick_volume; } int pocIdx = 0; double maxVal = -1; for(int i=0; i maxVal) { maxVal = hist[i]; pocIdx = i; } } out.POC = minP + pocIdx * stepSize; double vaVol = totalVol * 0.70; double curVol = maxVal; int up = pocIdx, down = pocIdx; while(curVol < vaVol) { double vUp = (up < steps-1) ? hist[up+1] : 0; double vDown = (down > 0) ? hist[down-1] : 0; if(vUp > vDown) { if(up < steps-1) { up++; curVol += hist[up]; } else if(down > 0) { down--; curVol += hist[down]; } else break; } else { if(down > 0) { down--; curVol += hist[down]; } else if(up < steps-1) { up++; curVol += hist[up]; } else break; } } out.VAH = minP + up * stepSize; out.VAL = minP + down * stepSize; ArrayResize(out.Crests, 0); ArrayResize(out.Troughs, 0); for(int i=2; i hist[i-1] && v > hist[i+1] && v > maxVal * 0.1) { int s = ArraySize(out.Crests); ArrayResize(out.Crests, s+1); out.Crests[s] = minP + i * stepSize; } if(v < hist[i-1] && v < hist[i+1]) { int s = ArraySize(out.Troughs); ArrayResize(out.Troughs, s+1); out.Troughs[s] = minP + i * stepSize; } } out.valid = true; } // Precompute Data (Using Loaded Globals) void PrecomputeData(const MqlRates &rates[], int total) { int oldestIdx = total - 1; datetime oldestTime = rates[oldestIdx].time; baseTime = oldestTime - (oldestTime % 86400); int numDays = (int)((rates[0].time - baseTime) / 86400) + 5; ArrayResize(DayMap, numDays); for(int i=0; i=0; i--) { MqlDateTime dt; TimeToStruct(rates[i].time, dt); int dayIdx = GetDayIndex(rates[i].time); if(dt.day_of_year != currDay) { sumPV_d=0; sumV_d=0; sumSqPV_d=0; currDay = dt.day_of_year; } int weekNum = (int)(rates[i].time / 604800); if(weekNum != currWeek) { sumPV_w=0; sumV_w=0; sumSqPV_w=0; currWeek = weekNum; } if(dt.mon != currMonth) { sumPV_m=0; sumV_m=0; sumSqPV_m=0; currMonth = dt.mon; } double typPrice = (rates[i].high + rates[i].low + rates[i].close) / 3.0; double vol = (double)rates[i].tick_volume; sumPV_d += typPrice * vol; sumV_d += vol; sumSqPV_d += vol * typPrice * typPrice; VWAPBuffer[i].vwap_d = (sumV_d > 0) ? sumPV_d / sumV_d : typPrice; VWAPBuffer[i].std_d = MathSqrt(MathMax(0, (sumV_d > 0) ? (sumSqPV_d / sumV_d) - (VWAPBuffer[i].vwap_d * VWAPBuffer[i].vwap_d) : 0)); sumPV_w += typPrice * vol; sumV_w += vol; sumSqPV_w += vol * typPrice * typPrice; VWAPBuffer[i].vwap_w = (sumV_w > 0) ? sumPV_w / sumV_w : typPrice; VWAPBuffer[i].std_w = MathSqrt(MathMax(0, (sumV_w > 0) ? (sumSqPV_w / sumV_w) - (VWAPBuffer[i].vwap_w * VWAPBuffer[i].vwap_w) : 0)); sumPV_m += typPrice * vol; sumV_m += vol; sumSqPV_m += vol * typPrice * typPrice; VWAPBuffer[i].vwap_m = (sumV_m > 0) ? sumPV_m / sumV_m : typPrice; VWAPBuffer[i].std_m = MathSqrt(MathMax(0, (sumV_m > 0) ? (sumSqPV_m / sumV_m) - (VWAPBuffer[i].vwap_m * VWAPBuffer[i].vwap_m) : 0)); int h = dt.hour; int m = dt.min; if(h >= gAsianStart && h < gAsianEnd) { if(DayMap[dayIdx].Asian.H == -1 || rates[i].high > DayMap[dayIdx].Asian.H) DayMap[dayIdx].Asian.H = rates[i].high; if(rates[i].low < DayMap[dayIdx].Asian.L) DayMap[dayIdx].Asian.L = rates[i].low; DayMap[dayIdx].Asian.Volume += vol; DayMap[dayIdx].Asian.valid = true; if(((h - gAsianStart)*60 + m) < 30) { if(DayMap[dayIdx].Asian.OR30_H == 0 || rates[i].high > DayMap[dayIdx].Asian.OR30_H) DayMap[dayIdx].Asian.OR30_H = rates[i].high; if(DayMap[dayIdx].Asian.OR30_L == 0 || rates[i].low < DayMap[dayIdx].Asian.OR30_L) DayMap[dayIdx].Asian.OR30_L = rates[i].low; DayMap[dayIdx].Asian.OR30_Vol += vol; } } if(h >= gLondonStart && h < gLondonEnd) { if(DayMap[dayIdx].London.H == -1 || rates[i].high > DayMap[dayIdx].London.H) DayMap[dayIdx].London.H = rates[i].high; if(rates[i].low < DayMap[dayIdx].London.L) DayMap[dayIdx].London.L = rates[i].low; DayMap[dayIdx].London.Volume += vol; DayMap[dayIdx].London.valid = true; if(((h - gLondonStart)*60 + m) < 30) { if(DayMap[dayIdx].London.OR30_H == 0 || rates[i].high > DayMap[dayIdx].London.OR30_H) DayMap[dayIdx].London.OR30_H = rates[i].high; if(DayMap[dayIdx].London.OR30_L == 0 || rates[i].low < DayMap[dayIdx].London.OR30_L) DayMap[dayIdx].London.OR30_L = rates[i].low; DayMap[dayIdx].London.OR30_Vol += vol; } } if(h >= gNYStart && h < gNYEnd) { if(DayMap[dayIdx].NY.H == -1 || rates[i].high > DayMap[dayIdx].NY.H) DayMap[dayIdx].NY.H = rates[i].high; if(rates[i].low < DayMap[dayIdx].NY.L) DayMap[dayIdx].NY.L = rates[i].low; DayMap[dayIdx].NY.Volume += vol; DayMap[dayIdx].NY.valid = true; if(((h - gNYStart)*60 + m) < 30) { if(DayMap[dayIdx].NY.OR30_H == 0 || rates[i].high > DayMap[dayIdx].NY.OR30_H) DayMap[dayIdx].NY.OR30_H = rates[i].high; if(DayMap[dayIdx].NY.OR30_L == 0 || rates[i].low < DayMap[dayIdx].NY.OR30_L) DayMap[dayIdx].NY.OR30_L = rates[i].low; DayMap[dayIdx].NY.OR30_Vol += vol; } } } int dayStartIdx = total-1; int currentDayIdx = GetDayIndex(rates[total-1].time); for(int i=total-1; i>=-1; i--) { int d = (i>=0) ? GetDayIndex(rates[i].time) : -1; if(d != currentDayIdx) { if(currentDayIdx >= 0 && currentDayIdx < numDays) { int idxStart = i + 1; CalcProfile(rates, idxStart, idxStart + (dayStartIdx - idxStart + 1), DayMap[currentDayIdx].Profile); if(DayMap[currentDayIdx].Asian.valid) { ArrayResize(list_AsianOR, ArraySize(list_AsianOR)+1); list_AsianOR[ArraySize(list_AsianOR)-1] = DayMap[currentDayIdx].Asian.OR30_Vol; ArrayResize(list_AsianTot, ArraySize(list_AsianTot)+1); list_AsianTot[ArraySize(list_AsianTot)-1] = DayMap[currentDayIdx].Asian.Volume; } if(DayMap[currentDayIdx].London.valid) { ArrayResize(list_LondonOR, ArraySize(list_LondonOR)+1); list_LondonOR[ArraySize(list_LondonOR)-1] = DayMap[currentDayIdx].London.OR30_Vol; ArrayResize(list_LondonTot, ArraySize(list_LondonTot)+1); list_LondonTot[ArraySize(list_LondonTot)-1] = DayMap[currentDayIdx].London.Volume; } if(DayMap[currentDayIdx].NY.valid) { ArrayResize(list_NYOR, ArraySize(list_NYOR)+1); list_NYOR[ArraySize(list_NYOR)-1] = DayMap[currentDayIdx].NY.OR30_Vol; ArrayResize(list_NYTot, ArraySize(list_NYTot)+1); list_NYTot[ArraySize(list_NYTot)-1] = DayMap[currentDayIdx].NY.Volume; } } currentDayIdx = d; dayStartIdx = i; } } AvgVol_Asian[0] = MathMean(list_AsianOR); AvgVol_Asian[1] = MathMean(list_AsianTot); AvgVol_London[0] = MathMean(list_LondonOR); AvgVol_London[1] = MathMean(list_LondonTot); AvgVol_NY[0] = MathMean(list_NYOR); AvgVol_NY[1] = MathMean(list_NYTot); } void OnDeinit(const int reason) { if(CheckPointer(Net) != POINTER_INVALID) delete Net; IndicatorRelease(handleEMA50); IndicatorRelease(handleEMA200); IndicatorRelease(handleATR_D1); } void OnTick() { datetime time = iTime(_Symbol, _Period, 0); if(time == lastBarTime) return; lastBarTime = time; int histDepth = 50000; MqlRates rates[]; ArraySetAsSeries(rates, true); double ema50[], ema200[]; ArraySetAsSeries(ema50, true); ArraySetAsSeries(ema200, true); if(CopyRates(_Symbol, PERIOD_H1, 0, histDepth, rates) < 2000 || CopyBuffer(handleEMA50, 0, 0, gLookback+1, ema50) < gLookback || CopyBuffer(handleEMA200, 0, 0, gLookback+1, ema200) < gLookback) return; // Refresh features int totalRates = ArraySize(rates); PrecomputeData(rates, totalRates); int featuresPerBar = 49; double inputs[]; ArrayResize(inputs, gLookback * featuresPerBar); for(int k=0; k= gAsianEnd) ? dayIdx : dayIdx - 1; if(idxA>=0 && DayMap[idxA].Asian.valid) { aH = DayMap[idxA].Asian.H; aL = DayMap[idxA].Asian.L; aVol = DayMap[idxA].Asian.Volume; aORH = DayMap[idxA].Asian.OR30_H; aORL = DayMap[idxA].Asian.OR30_L; aORVol = DayMap[idxA].Asian.OR30_Vol; } double lH=c, lL=c, lVol=0, lORH=c, lORL=c, lORVol=0; int idxL = (pHour >= gLondonEnd) ? dayIdx : dayIdx - 1; if(idxL>=0 && DayMap[idxL].London.valid) { lH = DayMap[idxL].London.H; lL = DayMap[idxL].London.L; lVol = DayMap[idxL].London.Volume; lORH = DayMap[idxL].London.OR30_H; lORL = DayMap[idxL].London.OR30_L; lORVol = DayMap[idxL].London.OR30_Vol; } double nH=c, nL=c, nVol=0, nORH=c, nORL=c, nORVol=0; int idxN = (pHour >= gNYEnd) ? dayIdx : dayIdx - 1; if(idxN>=0 && DayMap[idxN].NY.valid) { nH = DayMap[idxN].NY.H; nL = DayMap[idxN].NY.L; nVol = DayMap[idxN].NY.Volume; nORH = DayMap[idxN].NY.OR30_H; nORL = DayMap[idxN].NY.OR30_L; nORVol = DayMap[idxN].NY.OR30_Vol; } inputs[off+0] = (c - aORH)/c * 1000; inputs[off+1] = (c - aORL)/c * 1000; inputs[off+2] = (AvgVol_Asian[0]>0)?(aORVol/AvgVol_Asian[0]):0; inputs[off+3] = (c - aH)/c * 1000; inputs[off+4] = (c - aL)/c * 1000; inputs[off+5] = (AvgVol_Asian[1]>0)?(aVol/AvgVol_Asian[1]):0; inputs[off+6] = (c - lORH)/c * 1000; inputs[off+7] = (c - lORL)/c * 1000; inputs[off+8] = (AvgVol_London[0]>0)?(lORVol/AvgVol_London[0]):0; inputs[off+9] = (c - lH)/c * 1000; inputs[off+10] = (c - lL)/c * 1000; inputs[off+11] = (AvgVol_London[1]>0)?(lVol/AvgVol_London[1]):0; inputs[off+12] = (c - nORH)/c * 1000; inputs[off+13] = (c - nORL)/c * 1000; inputs[off+14] = (AvgVol_NY[0]>0)?(nORVol/AvgVol_NY[0]):0; inputs[off+15] = (c - nH)/c * 1000; inputs[off+16] = (c - nL)/c * 1000; inputs[off+17] = (AvgVol_NY[1]>0)?(nVol/AvgVol_NY[1]):0; inputs[off+18] = (c - ema50[p])/ema50[p] * 1000.0; inputs[off+19] = (c - ema200[p])/ema200[p] * 1000.0; inputs[off+20] = (ema50[p] - ema200[p])/ema200[p] * 1000.0; inputs[off+21] = (double)pDt.hour / 24.0; inputs[off+22] = (double)((pDt.min/15)) / 4.0; int mpIdx = dayIdx - 1; double poc=c, vah=c, val=c; double dCrest=0, dTrough=0; if(mpIdx>=0 && DayMap[mpIdx].Profile.valid) { poc = DayMap[mpIdx].Profile.POC; vah = DayMap[mpIdx].Profile.VAH; val = DayMap[mpIdx].Profile.VAL; double minD = 999999; for(int cx=0; cx 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: ", atrToUse); if(signal > InpConfidence && !PositionSelect(_Symbol)) { Trade.Buy(InpLotSize, _Symbol, 0, ask - distSL, ask + distTP, "NN Adv Buy"); } else if(signal < -InpConfidence && !PositionSelect(_Symbol)) { Trade.Sell(InpLotSize, _Symbol, 0, bid + distSL, bid - distTP, "NN Adv Sell"); } }