iForexSessions/iForexSessions.mq5
2025-09-19 20:19:50 +00:00

446 lines
39 KiB
MQL5

//+------------------------------------------------------------------+
//| iForexSessions.mq5 |
//| Copyright © 2018, Amr Ali |
//| https://www.mql5.com/en/users/amrali |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2018, Amr Ali"
#property link "https://www.mql5.com/en/users/amrali"
#property version "1.99"
#property description "Forex Sessions indicator"
#property description "Highlights the Forex Market Sessions"
#property description "The indicator assumes local \"wall clock\" trading hours of 8:00 AM - 5:00 PM in "
#property description "each Forex market, except in Sydney it is 7:00 AM - 4:00 PM or 9:00 AM - 6:00 PM."
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
#include <Canvas\Canvas.mqh>
#include "SessionHours.mqh"
//+------------------------------------------------------------------+
//--- input parameters
input int InpNumDays = 35; // Number of days to display
input color InpColorSydney = clrPurple; // Sydney session color
input color InpColorTokyo = clrTeal; // Tokyo session color
input color InpColorLondon = clrMaroon; // London session color
input color InpColorNewYork = clrDarkBlue; // NewYork session color
input bool InpShowLabels = true; // Show session labels
input bool InpShowVLines = true; // Show sydney-session start lines
input bool InpShowClock = true; // Show broker's clock (left lower corner)
input bool InpShowGrid = false; // Show grid in the chart
input bool InpUseGoldSymbol = true; // Load XAUUSD symbol for estimation of the server's TZ/DST
//+------------------------------------------------------------------+
//--- global variables
string obj_name_prefix=MQLInfoString(MQL_PROGRAM_NAME);
int WidthScreen;
int HeightScreen;
CCanvas canv;
bool canv_on=false;
bool ready_H1=false;
bool chart_grid;
enum ENUM_SESSIONS
{
SYD = 0,
TOK = 1,
LON = 2,
NYC = 3
};
struct SBrokerSession
{
datetime BeginBroker; // session time begin in broker time
datetime EndBroker; // session time end in broker time
};
SBrokerSession Sessions[][4]; // 2d array of 4 sessions for n days
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(Period()>PERIOD_H4)
{
Alert("This indicator works only on H4 charts and lower");
}
//--- timer
if(InpShowClock)
{
EventSetTimer(1);
}
//--- remember chart grid flag
ChartGetInteger(0,CHART_SHOW_GRID,chart_grid);
//--- set chart grid flag
ChartSetInteger(0,CHART_SHOW_GRID,InpShowGrid);
ChartRedraw();
//--- create canvas to output debug info
WidthScreen =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
HeightScreen=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
ChartSetInteger(0,CHART_FOREGROUND,false);
if(!canv.CreateBitmapLabel(0,0,"Canvas07",0,0,WidthScreen,HeightScreen,COLOR_FORMAT_ARGB_NORMALIZE))
Print("Error creating canvas: ",GetLastError());
canv.FontSet("Courier New",18);
canv.FontFlagsSet(FW_SEMIBOLD);
ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
//--- set the option of using Gold symbol for estimation of the server's TZ/DST
CTimeZoneInfo::SetUsingGoldSymbol(InpUseGoldSymbol);
//--- Initialization completed successfully
return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- delete timer
EventKillTimer();
//--- delete canvas
canv.Destroy();
//---- restore chart grid
ChartSetInteger(0,CHART_SHOW_GRID,chart_grid);
//--- delete objects
ObjectsDeleteAll(0,obj_name_prefix);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
ENUM_TIMEFRAMES P=Period();
if(P > PERIOD_H4)
return(0);
//--- make sure that H1 timeframe is loaded.
datetime timeH1=iTime(NULL,PERIOD_H1,0);
if(timeH1==0)
{
datetime times[];
if(CopyTime(Symbol(),PERIOD_H1,time[0],1,times)<=0)
{
ChartSetSymbolPeriod(0,NULL,P); // emulate a tick
}
ready_H1=false;
return(0);
}
ready_H1=true;
//--- draw only if a new hourly bar has arrived
static datetime prevtime;
if(timeH1 == prevtime)
return(0);
else
prevtime=timeH1;
//---
GetForexSessions(Sessions,InpNumDays);
DrawSessions(Sessions);
ChartRedraw();
//---
return(0);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id==CHARTEVENT_CHART_CHANGE)
{
if(ready_H1)
{
GetForexSessions(Sessions,InpNumDays);
DrawSessions(Sessions);
ChartRedraw();
}
WidthScreen =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
HeightScreen=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
canv.Resize(WidthScreen,HeightScreen);
}
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- moving the mouse on a chart while the "Ctrl" key is pressed
if(TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
{
int x =(int)lparam;
int y =(int)dparam;
datetime time =0;
double price =0;
int window=0;
//--- convert the X and Y coordinates in terms of time/price
if(ChartXYToTimePrice(0,x,y,window,time,price))
{
int i1=iBarShift(_Symbol,_Period,time);
datetime bartime = iTime(_Symbol,_Period,i1);
bool dst = CTimeZoneInfo::IsDaylightSavingsTime(ZONE_ID_BROKER, bartime);
//---
ChartTimePriceToXY(0,window,bartime,price,x,y);
canv.Erase(0);
canv.LineVertical(x,HeightScreen-1,0,ColorToARGB(clrRed));
canv.TextOut(WidthScreen-600,HeightScreen-170,"bar #"+IntegerToString(i1),ColorToARGB(dst ? clrYellow: clrOrange));
canv.TextOut(WidthScreen-600,HeightScreen-150,CTimeZoneInfo::FormatTimeForPlace(bartime, ZONE_ID_BROKER),ColorToARGB(dst ? clrYellow: clrOrange));
//---
ENUM_ZONE_ID ids[]= {ZONE_ID_SYDNEY, ZONE_ID_TOKYO, ZONE_ID_LONDON, ZONE_ID_NEWYORK};
for(int i = 0; i < ArraySize(ids); i++)
{
ENUM_ZONE_ID id = ids[i];
datetime converted = CTimeZoneInfo::ConvertTimeForPlace(bartime, ZONE_ID_BROKER, id);
dst = CTimeZoneInfo::IsDaylightSavingsTime(id, converted);
//PrintFormat("bar #%d | %s | %s", i1, CTimeZoneInfo::FormatTimeForPlace(bartime, ZONE_ID_BROKER), CTimeZoneInfo::FormatTimeForPlace(converted, id));
canv.TextOut(WidthScreen-600,HeightScreen-130+i*20,CTimeZoneInfo::FormatTimeForPlace(converted, id),ColorToARGB(dst ? clrYellow: clrOrange));
}
canv.Update();
canv_on=true;
}
}
//--- moving the mouse on a chart while the "Ctrl" key is released
else
if(canv_on)
{
canv.Erase(0);
canv.Update();
canv_on=false;
}
}
}
//+------------------------------------------------------------------+
//| Custom indicator timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
UpdateBrokerClock();
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Get forex market sessions for the specified num of days. |
//+------------------------------------------------------------------+
int GetForexSessions(SBrokerSession &BrokerSessions[][4], int numDays=9)
{
ArrayFree(BrokerSessions);
if(numDays <= 0)
return(0);
ArrayResize(BrokerSessions,numDays);
//--- Forex pairs start at 17:00 NY. Gold starts an hour later.
string beginNYC = "17:00";
if(StringSubstr(Symbol(),0,3)=="XAU"||StringSubstr(Symbol(),0,4)=="GOLD")
{
beginNYC = "18:00";
}
int day = 0;
int bars = iBars(NULL,PERIOD_H1);
for(int i = 0; i < bars; i++)
{
datetime bartime = iTime(NULL,PERIOD_H1,i);
datetime tNYC = CTimeZoneInfo::ConvertTimeForPlace(bartime, ZONE_ID_BROKER, ZONE_ID_NEWYORK);
if(TimeToString(tNYC,TIME_MINUTES) == beginNYC) // find the H1 bar matching Fx opening in NY.
{
//--- server time corresponding to forex opening at 5 pm in New York (or 6 pm for gold)
BrokerSessions[day][SYD].BeginBroker = bartime;
BrokerSessions[day][SYD].EndBroker = bartime + 9*PeriodSeconds(PERIOD_H1);
//--- set session hours to 8:00 am - 5:00 pm local time
datetime beginlocal = StringToTime(TimeToString(tNYC+PeriodSeconds(PERIOD_D1), TIME_DATE) + " " + "08:00");
datetime endlocal = StringToTime(TimeToString(tNYC+PeriodSeconds(PERIOD_D1), TIME_DATE) + " " + "17:00");
//--- conversion to broker time
BrokerSessions[day][TOK].BeginBroker = CTimeZoneInfo::ConvertTimeForPlace(beginlocal, ZONE_ID_TOKYO, ZONE_ID_BROKER);
BrokerSessions[day][TOK].EndBroker = CTimeZoneInfo::ConvertTimeForPlace(endlocal, ZONE_ID_TOKYO, ZONE_ID_BROKER);
BrokerSessions[day][LON].BeginBroker = CTimeZoneInfo::ConvertTimeForPlace(beginlocal, ZONE_ID_LONDON, ZONE_ID_BROKER);
BrokerSessions[day][LON].EndBroker = CTimeZoneInfo::ConvertTimeForPlace(endlocal, ZONE_ID_LONDON, ZONE_ID_BROKER);
BrokerSessions[day][NYC].BeginBroker = CTimeZoneInfo::ConvertTimeForPlace(beginlocal, ZONE_ID_NEWYORK, ZONE_ID_BROKER);
BrokerSessions[day][NYC].EndBroker = CTimeZoneInfo::ConvertTimeForPlace(endlocal, ZONE_ID_NEWYORK, ZONE_ID_BROKER);
//---
if(++day>=numDays)
break;
}
}
numDays = MathMin(numDays,day);
ArrayResize(BrokerSessions,numDays);
// ArrayPrint(BrokerSessions);
//---
return(numDays);
}
//+------------------------------------------------------------------+
//| Create vertical lines and colored rectangles for forex sessions |
//+------------------------------------------------------------------+
void DrawSessions(SBrokerSession &BrokerSessions[][4])
{
string obj_name, tooltip;
datetime time1, time2;
double price1, price2;
color sColor;
double high[];
double dayhigh;
bool weekstart;
//---
string session_names[]= {"Sydney","Tokyo","London","NewYork"};
string session_labels[]= {"SYD","TOK","LON","NYC"};
color session_colors[4];
session_colors[0]=InpColorSydney;
session_colors[1]=InpColorTokyo;
session_colors[2]=InpColorLondon;
session_colors[3]=InpColorNewYork;
//--- find the highest and lowest values of the chart
double max_price=ChartGetDouble(0,CHART_PRICE_MAX);
double min_price=ChartGetDouble(0,CHART_PRICE_MIN);
double height=(max_price-min_price)/70;
//--- delete objects
ObjectsDeleteAll(0,obj_name_prefix);
//---
for(int i = 0; i < ArrayRange(BrokerSessions, 0); i++)
{
if(InpShowVLines)
{
//--- Show sydney-session start lines
time1 = BrokerSessions[i][SYD].BeginBroker;
obj_name = obj_name_prefix+": "+CTimeZoneInfo::FormatTimeForPlace(time1, ZONE_ID_BROKER, false, false);
weekstart = (i+1 < ArrayRange(BrokerSessions, 0)) && (time1 - BrokerSessions[i+1][SYD].BeginBroker > 24*3600);
sColor = weekstart ? clrDarkOrange : clrWheat;
VLineCreate(obj_name,time1,sColor,STYLE_DASHDOTDOT);
//--- Show day of week text label
int shift = iBarShift(Symbol(),0,time1);
double low = iLow(Symbol(),0,shift);
string wkday = EnumToString((ENUM_DAY_OF_WEEK)(((uint)time1 / 86400 + 4) % 7));
TextCreate(obj_name+"_wkday",wkday,time1,low-50*_Point,sColor);
}
int copied=CopyHigh(Symbol(),PERIOD_H1,BrokerSessions[i][SYD].BeginBroker,BrokerSessions[i][NYC].EndBroker,high);
if(copied<=0)
continue;
dayhigh=high[ArrayMaximum(high)];
for(int s = 0; s < ArraySize(session_names); s++)
{
obj_name = obj_name_prefix+session_names[s]+"Rect"+(string)i;
time1 = BrokerSessions[i][s].BeginBroker;
time2 = BrokerSessions[i][s].EndBroker;
price1 = dayhigh + (height*1.3)*(4-s);
price2 = price1 - height;
sColor = session_colors[s];
tooltip = session_names[s]+" session from "+TimeToString(time1,TIME_MINUTES)+" to "+TimeToString(time2,TIME_MINUTES)+" (server time)";
RectangleCreate(obj_name,time1,price1,time2,price2,sColor,tooltip);
//---
if(InpShowLabels)
{
obj_name = obj_name_prefix+session_names[s]+"Text"+(string)i;
tooltip = session_labels[s]+" "+TimeToString(time1,TIME_MINUTES);
TextCreate(obj_name,tooltip,time1,price1-(height/2),clrWheat);
}
}
}
//---
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void UpdateBrokerClock()
{
string obj_name, text;
int x_, y_;
color sColor;
//---
int sec = CSessionHours::SecRemainingForex();
datetime forexCloseTime = CSessionHours::ForexCloseTime();
int shiftInSeconds = CheckTimeSync();
//---
string brokers_labels[4][2] =
{
{"Server Time : ", CTimeZoneInfo::FormatTimeForPlace(TimeTradeServer(), ZONE_ID_BROKER)},
{"Closing Time : ", CTimeZoneInfo::FormatTimeForPlace(forexCloseTime, ZONE_ID_BROKER)},
{"Remaining sec : ", StringFormat("%i sec = %s %s", sec, CSessionHours::SecondsToString(sec), sec ? sec >= 7200 ? "" : " (closing soon)" : " (market closed)")},
{"PC Time sync : ", shiftInSeconds < 30 ? "Ok" : StringFormat("Shift of pc time is %i sec - Check www.time.is", shiftInSeconds)}
};
//---
for(int i = 0; i < ArrayRange(brokers_labels,0); i++)
for(int j = 0; j < ArrayRange(brokers_labels,1); j++)
{
obj_name = obj_name_prefix+"BrokerLabel_"+(string)(i+4*j);
text = brokers_labels[i][j];
x_ = 12+112*j;
y_ = 72-14*i;
sColor = sec != 0 ? clrWheat : clrDarkGray;
LabelCreate(obj_name,text,x_,y_,sColor);
}
}
//+------------------------------------------------------------------+
//| Check the time synchronization of the local computer. |
//+------------------------------------------------------------------+
int CheckTimeSync()
{
static datetime current, before;
static int shiftInSeconds = 0;
before = current;
current = TimeCurrent();
//--- if it is the first call of the function
if(before == 0)
return(0);
//--- if the server is not "frozen" due to weekends/holidays
if(current != before)
{
shiftInSeconds = (int)(TimeTradeServer() - current);
}
return(shiftInSeconds);
}
//+------------------------------------------------------------------+
//| Create the vertical line |
//+------------------------------------------------------------------+
void VLineCreate(string name, datetime time, color clr, ENUM_LINE_STYLE style=STYLE_SOLID, int width=1)
{
ObjectCreate(0,name,OBJ_VLINE,0,time,0);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_STYLE,style);
ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
}
//+------------------------------------------------------------------+
//| Create rectangle by the given coordinates |
//+------------------------------------------------------------------+
void RectangleCreate(string name, datetime time1, double price1, datetime time2, double price2, color clr, string tooltip)
{
ObjectCreate(0,name,OBJ_RECTANGLE,0,time1,price1,time2,price2);
ObjectSetString(0,name,OBJPROP_TOOLTIP,tooltip);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
ObjectSetInteger(0,name,OBJPROP_FILL,true);
ObjectSetInteger(0,name,OBJPROP_BACK,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
}
//+------------------------------------------------------------------+
//| Creating Text object |
//+------------------------------------------------------------------+
void TextCreate(string name, string text, datetime time, double price, color clr=clrWhiteSmoke)
{
ObjectCreate(0,name,OBJ_TEXT,0,time,price);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT);
ObjectSetDouble(0,name,OBJPROP_ANGLE,0.00);
ObjectSetString(0,name,OBJPROP_FONT,"Lucida Console");
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
ObjectSetInteger(0,name,OBJPROP_BACK,true);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
}
//+------------------------------------------------------------------+
//| Create a text label |
//+------------------------------------------------------------------+
void LabelCreate(string name, string text, int x, int y, color clr=clrWhiteSmoke)
{
ObjectCreate(0,name,OBJ_LABEL,0,0,0);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER);
ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT);
ObjectSetDouble(0,name,OBJPROP_ANGLE,0.00);
ObjectSetString(0,name,OBJPROP_FONT,"Lucida Console");
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
}
//+------------------------------------------------------------------+