//+------------------------------------------------------------------+ //| SessionRevEA.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property strict #include //--- Input Parameters input string InpSessionAsian = "00:00-09:00"; // Asian Session Time input string InpSessionLondonPOC = "09:00-11:00"; // London POC Build Window input int InpTicksPerBin = 10; // Ticks per Volume Bin input int InpSLOffsetPips = 10; // SL Offset from London POC (Pips) input double InpLotSize = 0.1; // Lot Size input int InpMagicNumber = 123456; // Magic Number //--- Global Variables CTrade m_trade; datetime m_last_trade_date = 0; double m_asian_poc = 0; double m_london_poc = 0; int m_asian_start_h, m_asian_start_m; int m_asian_end_h, m_asian_end_m; int m_london_start_h, m_london_start_m; int m_london_end_h, m_london_end_m; bool m_asian_ready = false; bool m_london_ready = false; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { m_trade.SetExpertMagicNumber(InpMagicNumber); if(!ParseSession(InpSessionAsian, m_asian_start_h, m_asian_start_m, m_asian_end_h, m_asian_end_m) || !ParseSession(InpSessionLondonPOC, m_london_start_h, m_london_start_m, m_london_end_h, m_london_end_m)) { Print("Error: Invalid session time format. Use HH:MM-HH:MM"); return(INIT_PARAMETERS_INCORRECT); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { MqlDateTime dt; TimeCurrent(dt); datetime today = StringToTime(IntegerToString(dt.year)+"."+IntegerToString(dt.mon)+"."+IntegerToString(dt.day)); // Reset for new day if(m_last_trade_date != today) { // We don't reset the date here, only when a trade is TAKEN // But we should reset POC readiness at the start of Asian or end of day if(dt.hour == 0 && dt.min == 0) { m_asian_ready = false; m_london_ready = false; m_asian_poc = 0; m_london_poc = 0; } } // Calculate Asian POC if it just ended if(!m_asian_ready && IsTimePassed(m_asian_end_h, m_asian_end_m)) { datetime start_time = today + m_asian_start_h * 3600 + m_asian_start_m * 60; datetime end_time = today + m_asian_end_h * 3600 + m_asian_end_m * 60; // Handle overnight Asian session if(end_time <= start_time) start_time -= 86400; m_asian_poc = CalculatePOC(start_time, end_time); if(m_asian_poc > 0) { m_asian_ready = true; Print("Asian POC established at: ", m_asian_poc); } } // Calculate London POC if its build window just ended if(m_asian_ready && !m_london_ready && IsTimePassed(m_london_end_h, m_london_end_m)) { datetime start_time = today + m_london_start_h * 3600 + m_london_start_m * 60; datetime end_time = today + m_london_end_h * 3600 + m_london_end_m * 60; m_london_poc = CalculatePOC(start_time, end_time); if(m_london_poc > 0) { m_london_ready = true; Print("London POC established at: ", m_london_poc); } } // Trade Execution Logic if(m_asian_ready && m_london_ready && m_last_trade_date != today) { // Check if we are still within a reasonable time for London session trades // (e.g., before NY session starts or London ends) if(dt.hour >= 20) return; // Stop trading late in the day if(PositionsTotal() == 0) { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double sl_dist = InpSLOffsetPips * 10 * point; // Short Entry: London POC established ABOVE Asian POC, and price drops below London POC towards Asian POC if(m_london_poc > m_asian_poc) { if(bid < m_london_poc && bid > m_asian_poc) { double sl = m_london_poc + sl_dist; double tp = m_asian_poc; if(m_trade.Sell(InpLotSize, _Symbol, bid, sl, tp, "Session Reversion Short")) { m_last_trade_date = today; Print("Short trade opened. Target: Asian POC ", tp); } } } // Long Entry: London POC established BELOW Asian POC, and price rises above London POC towards Asian POC else if(m_london_poc < m_asian_poc) { if(ask > m_london_poc && ask < m_asian_poc) { double sl = m_london_poc - sl_dist; double tp = m_asian_poc; if(m_trade.Buy(InpLotSize, _Symbol, ask, sl, tp, "Session Reversion Long")) { m_last_trade_date = today; Print("Long trade opened. Target: Asian POC ", tp); } } } } } } //+------------------------------------------------------------------+ //| Parse Session String (HH:MM-HH:MM) | //+------------------------------------------------------------------+ bool ParseSession(string session, int &sh, int &sm, int &eh, int &em) { string parts[]; if(StringSplit(session, '-', parts) != 2) return false; string start_parts[]; string end_parts[]; if(StringSplit(parts[0], ':', start_parts) != 2) return false; if(StringSplit(parts[1], ':', end_parts) != 2) return false; sh = (int)StringToInteger(start_parts[0]); sm = (int)StringToInteger(start_parts[1]); eh = (int)StringToInteger(end_parts[0]); em = (int)StringToInteger(end_parts[1]); return true; } //+------------------------------------------------------------------+ //| Check if specified hour/min has passed in current day | //+------------------------------------------------------------------+ bool IsTimePassed(int h, int m) { MqlDateTime dt; TimeCurrent(dt); int current_mins = dt.hour * 60 + dt.min; int target_mins = h * 60 + m; return (current_mins >= target_mins); } //+------------------------------------------------------------------+ //| Calculate Point of Control (POC) for a time range | //+------------------------------------------------------------------+ double CalculatePOC(datetime start_time, datetime end_time) { int start_bar = iBarShift(_Symbol, _Period, start_time); int end_bar = iBarShift(_Symbol, _Period, end_time); if(start_bar < 0 || end_bar < 0) return 0; // Ensure order int s_bar = (start_bar > end_bar) ? start_bar : end_bar; int e_bar = (start_bar > end_bar) ? end_bar : start_bar; double min_price = 0, max_price = 0; for(int i = e_bar; i <= s_bar; i++) { double h = iHigh(_Symbol, _Period, i); double l = iLow(_Symbol, _Period, i); if(i == e_bar) { min_price = l; max_price = h; } else { if(l < min_price) min_price = l; if(h > max_price) max_price = h; } } double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double bin_size = tick_size * InpTicksPerBin; if(bin_size <= 0) return 0; int bin_count = (int)((max_price - min_price) / bin_size) + 1; if(bin_count <= 0) return 0; struct Bin { double price; long vol; }; Bin bins[]; ArrayResize(bins, bin_count); for(int i = 0; i < bin_count; i++) { bins[i].price = min_price + i * bin_size; bins[i].vol = 0; } for(int i = e_bar; i <= s_bar; i++) { double h = iHigh(_Symbol, _Period, i); double l = iLow(_Symbol, _Period, i); long vol = iTickVolume(_Symbol, _Period, i); int h_idx = (int)((h - min_price) / bin_size); int l_idx = (int)((l - min_price) / bin_size); if(h_idx >= bin_count) h_idx = bin_count - 1; if(l_idx < 0) l_idx = 0; int bars_covered = h_idx - l_idx + 1; long vol_per_bin = vol / bars_covered; for(int j = l_idx; j <= h_idx; j++) bins[j].vol += vol_per_bin; } long max_vol = -1; int poc_idx = 0; for(int i = 0; i < bin_count; i++) { if(bins[i].vol > max_vol) { max_vol = bins[i].vol; poc_idx = i; } } return bins[poc_idx].price + bin_size / 2.0; } //+------------------------------------------------------------------+