mql5/Experts/SessionRevEA.mq5

260 lignes
9,5 Kio
MQL5
Brut Lien permanent Vue normale Historique

2026-02-05 13:48:34 +03:00
//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
//--- 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;
}
//+------------------------------------------------------------------+