session-levels/mql4/Indicators/SessionLevels/SessionLevels.mq4
Allan Niñal Allan Niñal bf918f6770 Free source regenerated: pop-up and push on by default
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:28:54 +08:00
629 Zeilen
24 KiB
MQL4
//+------------------------------------------------------------------+
//| SessionLevels.mq4 |
//| Copyright 2026, Allan Niñal |
//| https://www.allanninal.dev/mql5 |
//+------------------------------------------------------------------+
//| Trading sessions (Asia, London, New York) as translucent boxes |
//| with their high and low, plus the previous day and previous week |
//| high/low and today's open as lines. Session times are entered in |
//| New York time and converted per bar from the broker's clock, so |
//| daylight saving is right on any broker. A trading day ends at |
//| the 17:00 New York close. |
//| Alerts (closed bars only, each at most once per trading day): |
//| close above the previous day high / below the previous day low, |
//| and after the Asia session, close above its high / below its low.|
//| MT4 edition: the same calculation, inputs and 7 iCustom buffers |
//| as the MT5 one. MT4 has no transparency, so a filled box is the |
//| session colour blended into the chart background. |
//+------------------------------------------------------------------+
// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
#property copyright "Copyright 2026, Allan Niñal <https://www.allanninal.dev/mql5>"
#property link "https://www.allanninal.dev/mql5"
#property version "1.1"
#property description "Asia / London / New York session boxes, previous day and week high/low, today's open. Times in New York time, DST-safe."
#property description "Alerts on closed bars: break of the previous day high/low and of the Asia range. Non-repainting."
#property indicator_chart_window
#property indicator_buffers 7
enum ENUM_SL_CLOCK
{
SL_CLOCK_AUTO=0, // Auto: detect from the live server time
SL_CLOCK_NY=1, // New York close: UTC+2/+3 with US daylight saving
SL_CLOCK_EET=2, // UTC+2/+3 with European daylight saving
SL_CLOCK_FIXED=3 // Fixed offset from UTC (set below), no daylight saving
};
input int InpDays = 5; // Days to draw
input bool InpShowAsia = true; // Draw Asia
input string InpAsiaStart = "19:00"; // Asia start HH:MM
input string InpAsiaEnd = "04:00"; // Asia end HH:MM
input color InpAsiaColor = 16748574; // Asia colour
input bool InpShowLondon = true; // Draw London
input string InpLondonStart = "03:00"; // London start HH:MM
input string InpLondonEnd = "12:00"; // London end HH:MM
input color InpLondonColor = 42495; // London colour
input bool InpShowNY = true; // Draw New York
input string InpNYStart = "08:00"; // New York start HH:MM
input string InpNYEnd = "17:00"; // New York end HH:MM
input color InpNYColor = 7451452; // New York colour
input bool InpSessionLabels = true; // Label each box
input bool InpFillBoxes = true; // Fill the boxes (translucent)
input bool InpShowPD = true; // Previous day high/low
input bool InpShowPW = true; // Previous week high/low
input bool InpShowOpen = true; // Today's open
input color InpPDColor = 55295; // Previous day colour
input color InpPWColor = 14053594; // Previous week colour
input color InpOpenColor = 12632256; // Today's open colour
input bool InpLevelLabels = true; // Price labels on the levels
input int InpLevelWidth = 1; // Line width
input ENUM_SL_CLOCK InpClock = SL_CLOCK_AUTO; // Broker server clock
input int InpFixedUTC = 0; // Broker offset from UTC, hours (Fixed offset only)
input bool InpAlertPDH = true; // Close above previous day high
input bool InpAlertPDL = true; // Close below previous day low
input bool InpAlertAsiaH = true; // Close above Asia high (after Asia)
input bool InpAlertAsiaL = true; // Close below Asia low (after Asia)
#include "SignalAlerts.mqh"
#include "ObjSync.mqh"
#include <ChartKit/CkDebug.mqh>
input bool InpDebug = false; // Debug log for bug reports (Experts tab)
//--- buffers shown to the outside
double g_pdh[],g_pdl[],g_pwh[],g_pwl[],g_ash[],g_asl[],g_sig[];
//--- internal state, one value per bar so a recalculation can restart from any bar
double g_nyw[]; // New York wall time of the bar (seconds)
double g_dk[]; // trading-day number (New York close clock)
double g_wk[]; // trading-week number
double g_dhi[],g_dlo[],g_dop[]; // the day so far
double g_whi[],g_wlo[]; // the week so far
double g_aown[]; // owner date of the running Asia instance
double g_adk[]; // trading day the running Asia instance belongs to
double g_ahi[],g_alo[];
double g_flag[]; // signals already given today: 1 PDH, 2 PDL, 4 Asia high, 8 Asia low
const string PRODUCT="Session Levels";
const int NY_CLOSE_SHIFT=7*3600; // New York-close clock minus New York wall clock
ENUM_SL_CLOCK g_clock=SL_CLOCK_NY;
int g_fixed_off=0;
bool g_native=true;
long g_sh_hour=-1;
int g_sh_val=0;
int g_s_start[3],g_s_end[3];
bool g_s_on[3];
color g_s_clr[3];
string g_s_name[3]={"Asia","London","New York"};
string g_pfx="";
int g_obj_fail=0,g_dbg_fail=0; // objects the chart refused in the last pass / last reported
datetime g_draw_bar=0;
uint g_draw_tick=0;
double g_draw_hi=0,g_draw_lo=0;
int g_secs=60;
bool g_no_draw=false; // set in OnInit: tester without visual mode (no chart to draw on)
//+------------------------------------------------------------------+
//| Broker clock -> New York time (ported from Gold Trend Sentinel) |
//+------------------------------------------------------------------+
//--- Sunday on or after `day` of `month` in year `y`, at `hour` UTC
datetime SundayFrom(const int y,const int month,const int day,const int hour)
{
MqlDateTime t;
ZeroMemory(t);
t.year=y;
t.mon=month;
t.day=day;
datetime d=StructToTime(t);
TimeToStruct(d,t);
return(d+(datetime)(((7-t.day_of_week)%7)*86400+hour*3600));
}
//--- US daylight saving (second Sunday of March 07:00 UTC to first Sunday of November 06:00 UTC)
bool UsDst(const datetime utc)
{
MqlDateTime t;
TimeToStruct(utc,t);
return(utc>=SundayFrom(t.year,3,8,7) && utc<SundayFrom(t.year,11,1,6));
}
//--- EU summer time (last Sunday of March to last Sunday of October, 01:00 UTC)
bool EuDst(const datetime utc)
{
MqlDateTime t;
TimeToStruct(utc,t);
return(utc>=SundayFrom(t.year,3,25,1) && utc<SundayFrom(t.year,10,25,1));
}
int NyOffset(const datetime utc) { return(UsDst(utc) ? 3*3600 : 2*3600); }
//--- the broker's offset from UTC at server time `srv`
int BrokerOffset(const datetime srv)
{
if(g_clock==SL_CLOCK_FIXED)
return(g_fixed_off);
if(g_clock==SL_CLOCK_EET)
return(EuDst(srv-2*3600) ? 3*3600 : 2*3600);
return(NyOffset(srv-2*3600));
}
//--- seconds to add to server time to get New York-close time (cached per hour: offsets change on the hour)
int Shift(const datetime srv)
{
if(g_native)
return(0);
long h=(long)srv/3600;
if(h!=g_sh_hour)
{
int b=BrokerOffset(srv);
g_sh_val=NyOffset(srv-b)-b;
g_sh_hour=h;
}
return(g_sh_val);
}
//--- decide the clock once: the inputs, or the live server time against UTC (the tester can't tell)
void ResolveClock(void)
{
g_clock=InpClock;
g_fixed_off=InpFixedUTC*3600;
if(InpClock==SL_CLOCK_AUTO)
{
g_clock=SL_CLOCK_NY;
if(!IsTesting())
{
datetime gmt=TimeGMT();
//--- MT4 has no TimeTradeServer(): the last tick's server time; stale while the market is closed, so an
//--- offset that is no real UTC offset keeps the New York close assumption
int off=(int)MathRound((double)(TimeCurrent()-gmt)/1800.0)*1800;
if(off<-12*3600 || off>14*3600 || off==NyOffset(gmt))
g_clock=SL_CLOCK_NY;
else
if(off==(EuDst(gmt) ? 3*3600 : 2*3600))
g_clock=SL_CLOCK_EET;
else
{
g_clock=SL_CLOCK_FIXED;
g_fixed_off=off;
}
}
}
g_native=(g_clock==SL_CLOCK_NY);
g_sh_hour=-1;
}
//+------------------------------------------------------------------+
//| Helpers |
//+------------------------------------------------------------------+
//--- "HH:MM" -> minutes since midnight, -1 when invalid
int ParseHM(const string s)
{
string p[];
if(StringSplit(s,':',p)!=2)
return(-1);
int h=(int)StringToInteger(p[0]),m=(int)StringToInteger(p[1]);
if(h<0 || h>23 || m<0 || m>59)
return(-1);
return(h*60+m);
}
//--- is minute-of-day `m` inside [s,e) ? A session with s>e crosses midnight
bool InSession(const int m,const int s,const int e)
{
if(s==e)
return(false);
if(s<e)
return(m>=s && m<e);
return(m>=s || m<e);
}
//--- owner date of a session bar: a session that crosses midnight belongs to the day it ends on
long OwnerDate(const long nyw,const int m,const int s,const int e)
{
long d=nyw/86400;
return((s>e && m>=s) ? d+1 : d);
}
int OnInit()
{
CkDebugStart(PRODUCT,"1.1",InpDebug,StringFormat("days %d, sessions Asia %d %s-%s, London %d %s-%s, NY %d %s-%s, PD %d, PW %d, open %d, clock %d (UTC %d)",
InpDays,InpShowAsia,InpAsiaStart,InpAsiaEnd,InpShowLondon,InpLondonStart,InpLondonEnd,InpShowNY,InpNYStart,InpNYEnd,
InpShowPD,InpShowPW,InpShowOpen,(int)InpClock,InpFixedUTC));
g_s_start[0]=ParseHM(InpAsiaStart); g_s_end[0]=ParseHM(InpAsiaEnd);
g_s_start[1]=ParseHM(InpLondonStart); g_s_end[1]=ParseHM(InpLondonEnd);
g_s_start[2]=ParseHM(InpNYStart); g_s_end[2]=ParseHM(InpNYEnd);
for(int s=0; s<3; s++)
if(g_s_start[s]<0 || g_s_end[s]<0)
{
Print("Session Levels: session times must be HH:MM (00:00 to 23:59)");
CkDbg("init refused: session times");
return(INIT_PARAMETERS_INCORRECT);
}
g_s_on[0]=InpShowAsia; g_s_clr[0]=InpAsiaColor;
g_s_on[1]=InpShowLondon; g_s_clr[1]=InpLondonColor;
g_s_on[2]=InpShowNY; g_s_clr[2]=InpNYColor;
if(InpDays<1 || InpLevelWidth<1 || InpLevelWidth>5 || InpFixedUTC<-12 || InpFixedUTC>14)
{
Print("Session Levels: days to draw at least 1, line width 1-5, broker offset from UTC -12 to 14 hours");
CkDbg("init refused: days, line width or UTC offset");
return(INIT_PARAMETERS_INCORRECT);
}
IndicatorBuffers(20);
SetIndexBuffer(0,g_pdh,INDICATOR_DATA);
SetIndexBuffer(1,g_pdl,INDICATOR_DATA);
SetIndexBuffer(2,g_pwh,INDICATOR_DATA);
SetIndexBuffer(3,g_pwl,INDICATOR_DATA);
SetIndexBuffer(4,g_ash,INDICATOR_DATA);
SetIndexBuffer(5,g_asl,INDICATOR_DATA);
SetIndexBuffer(6,g_sig,INDICATOR_DATA);
SetIndexBuffer(7,g_nyw,INDICATOR_CALCULATIONS);
SetIndexBuffer(8,g_dk,INDICATOR_CALCULATIONS);
SetIndexBuffer(9,g_wk,INDICATOR_CALCULATIONS);
SetIndexBuffer(10,g_dhi,INDICATOR_CALCULATIONS);
SetIndexBuffer(11,g_dlo,INDICATOR_CALCULATIONS);
SetIndexBuffer(12,g_dop,INDICATOR_CALCULATIONS);
SetIndexBuffer(13,g_whi,INDICATOR_CALCULATIONS);
SetIndexBuffer(14,g_wlo,INDICATOR_CALCULATIONS);
SetIndexBuffer(15,g_aown,INDICATOR_CALCULATIONS);
SetIndexBuffer(16,g_adk,INDICATOR_CALCULATIONS);
SetIndexBuffer(17,g_ahi,INDICATOR_CALCULATIONS);
SetIndexBuffer(18,g_alo,INDICATOR_CALCULATIONS);
SetIndexBuffer(19,g_flag,INDICATOR_CALCULATIONS);
string names[7]={"Prev day high","Prev day low","Prev week high","Prev week low","Asia high (so far)",
"Asia low (so far)","Signal"};
for(int p=0; p<20; p++)
{
SetIndexStyle(p,DRAW_NONE);
SetIndexEmptyValue(p,EMPTY_VALUE);
SetIndexLabel(p,p<7 ? names[p] : NULL);
}
IndicatorShortName("Session Levels");
IndicatorDigits(_Digits);
ResolveClock();
CkDbg(StringFormat("clock resolved: mode %d, offset from UTC %d s, %s",(int)g_clock,g_fixed_off,g_native ? "server time is New York close" : "converted per bar"));
if(IsTesting() && InpClock==SL_CLOCK_AUTO)
Print("Session Levels: the Strategy Tester can't detect the broker's clock; New York close is assumed. ",
"Set 'Broker server clock' for a broker on another clock.");
g_secs=PeriodSeconds();
g_pfx="SLV"+IntegerToString((int)(GetMicrosecondCount()%1000000))+"_";
g_draw_bar=0;
g_obj_fail=0;
g_dbg_fail=0;
g_no_draw=(IsTesting() && !IsVisualMode());
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
if(g_pfx!="")
{
ObjectsDeleteAll(0,g_pfx);
ChartRedraw();
}
}
//+------------------------------------------------------------------+
//| Drawing |
//+------------------------------------------------------------------+
void PutObj(const string name,const ENUM_OBJECT type,const datetime t1,const double p1,const datetime t2,const double p2)
{
if(!OsPlace(name,type,2,t1,p1,t2,p2))
{
g_obj_fail++;
return;
}
OsInt(name,OBJPROP_BACK,true);
}
void PutText(const string name,const datetime t,const double price,const string text,const color clr,const ENUM_ANCHOR_POINT anchor)
{
if(!OsPlace(name,OBJ_TEXT,1,t,price,0,0))
{
g_obj_fail++;
return;
}
OsStr(name,OBJPROP_TEXT,text);
OsInt(name,OBJPROP_COLOR,clr);
OsInt(name,OBJPROP_ANCHOR,anchor);
OsInt(name,OBJPROP_FONTSIZE,8);
}
//--- MT4 cannot draw a translucent rectangle: blend the colour into the chart background by `alpha` (0-255)
color Blend(const color c,const int alpha)
{
color bg=(color)ChartGetInteger(0,CHART_COLOR_BACKGROUND);
int r=((c&0xFF)*alpha+(bg&0xFF)*(255-alpha))/255;
int g=(((c>>8)&0xFF)*alpha+((bg>>8)&0xFF)*(255-alpha))/255;
int b=(((c>>16)&0xFF)*alpha+((bg>>16)&0xFF)*(255-alpha))/255;
return((color)(r|(g<<8)|(b<<16)));
}
void DrawBox(const int s,const long owner,const datetime t1,const datetime t2,const double hi,const double lo)
{
string nm=g_pfx+"B"+IntegerToString(s)+"_"+IntegerToString(owner);
PutObj(nm,OBJ_RECTANGLE,t1,hi,t2,lo);
OsInt(nm,OBJPROP_COLOR,InpFillBoxes ? Blend(g_s_clr[s],60) : g_s_clr[s]);
OsInt(nm,OBJPROP_FILL,InpFillBoxes);
OsInt(nm,OBJPROP_BACK,InpFillBoxes); // MT4 fills a rectangle drawn in the background
OsInt(nm,OBJPROP_WIDTH,1);
if(InpSessionLabels)
PutText(g_pfx+"T"+IntegerToString(s)+"_"+IntegerToString(owner),t1,hi,g_s_name[s],g_s_clr[s],ANCHOR_LEFT_LOWER);
}
void DrawLevel(const string tag,const string label,const double price,const color clr,const int a,const int b,
const bool current,const datetime &time[],const int last,const ENUM_LINE_STYLE style)
{
if(price==EMPTY_VALUE || price<=0)
return;
datetime t1=time[a],t2=(current ? time[last]+g_secs : time[b]+g_secs);
string nm=g_pfx+"L"+tag+"_"+IntegerToString((long)time[a]);
PutObj(nm,OBJ_TREND,t1,price,t2,price);
OsInt(nm,OBJPROP_COLOR,clr);
OsInt(nm,OBJPROP_WIDTH,InpLevelWidth);
OsInt(nm,OBJPROP_STYLE,style);
OsInt(nm,OBJPROP_RAY_RIGHT,current);
if(current && InpLevelLabels)
PutText(g_pfx+"P"+tag,time[last]+3*g_secs,price,label+" "+DoubleToString(price,_Digits),clr,ANCHOR_LEFT);
}
//--- redraw everything for the last InpDays days (objects are deleted first, so old days disappear)
void Draw(const int rates_total,const datetime &time[],const double &high[],const double &low[])
{
int last=rates_total-1;
bool intraday=(g_secs<=PeriodSeconds(PERIOD_H4));
int days=(intraday ? MathMin(InpDays,60) : 1);
OsBegin();
g_obj_fail=0;
//--- first bar of the oldest day to draw
int first=last,cnt=1;
for(int k=last; k>0; k--)
{
if(g_dk[k-1]!=g_dk[k])
{
if(cnt>=days)
break;
cnt++;
}
first=k-1;
}
//--- session boxes
if(intraday)
for(int s=0; s<3; s++)
{
if(!g_s_on[s])
continue;
bool open=false;
long owner=0;
int st=0,en=0;
double hi=0,lo=0;
for(int j=first; j<=last; j++)
{
int m=(int)(((long)g_nyw[j]%86400)/60);
if(InSession(m,g_s_start[s],g_s_end[s]))
{
long ow=OwnerDate((long)g_nyw[j],m,g_s_start[s],g_s_end[s]);
if(open && ow==owner)
{
en=j;
hi=MathMax(hi,high[j]);
lo=MathMin(lo,low[j]);
}
else
{
if(open)
DrawBox(s,owner,time[st],time[en]+g_secs,hi,lo);
open=true;
owner=ow;
st=j;
en=j;
hi=high[j];
lo=low[j];
}
}
else
if(open)
{
DrawBox(s,owner,time[st],time[en]+g_secs,hi,lo);
open=false;
}
}
if(open)
DrawBox(s,owner,time[st],time[en]+g_secs,hi,lo);
}
//--- level lines, one segment per day; the current day is a ray to the right
int a=first;
while(a<=last)
{
int b=a;
while(b<last && g_dk[b+1]==g_dk[a])
b++;
bool cur=(b==last);
if(InpShowPD)
{
DrawLevel("PDH","PDH",g_pdh[a],InpPDColor,a,b,cur,time,last,STYLE_SOLID);
DrawLevel("PDL","PDL",g_pdl[a],InpPDColor,a,b,cur,time,last,STYLE_SOLID);
}
if(InpShowPW)
{
DrawLevel("PWH","PWH",g_pwh[a],InpPWColor,a,b,cur,time,last,STYLE_DASH);
DrawLevel("PWL","PWL",g_pwl[a],InpPWColor,a,b,cur,time,last,STYLE_DASH);
}
if(InpShowOpen && intraday)
DrawLevel("OP","Open",g_dop[a],InpOpenColor,a,b,cur,time,last,STYLE_DOT);
a=b+1;
}
OsEnd(g_pfx);
if(g_obj_fail!=g_dbg_fail)
{
g_dbg_fail=g_obj_fail;
CkDbg(StringFormat("the chart refused %d object(s), error %d",g_obj_fail,GetLastError()));
}
g_draw_bar=time[last];
g_draw_tick=GetTickCount();
g_draw_hi=high[last];
g_draw_lo=low[last];
ChartRedraw();
}
//+------------------------------------------------------------------+
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[])
{
if(rates_total<2)
return(0);
//--- count bars from the oldest, as MT5 does (prices and every buffer)
ArraySetAsSeries(time,false);
ArraySetAsSeries(open,false);
ArraySetAsSeries(high,false);
ArraySetAsSeries(low,false);
ArraySetAsSeries(close,false);
ArraySetAsSeries(g_pdh,false); ArraySetAsSeries(g_pdl,false); ArraySetAsSeries(g_pwh,false);
ArraySetAsSeries(g_pwl,false); ArraySetAsSeries(g_ash,false); ArraySetAsSeries(g_asl,false);
ArraySetAsSeries(g_sig,false); ArraySetAsSeries(g_nyw,false); ArraySetAsSeries(g_dk,false);
ArraySetAsSeries(g_wk,false); ArraySetAsSeries(g_dhi,false); ArraySetAsSeries(g_dlo,false);
ArraySetAsSeries(g_dop,false); ArraySetAsSeries(g_whi,false); ArraySetAsSeries(g_wlo,false);
ArraySetAsSeries(g_aown,false); ArraySetAsSeries(g_adk,false); ArraySetAsSeries(g_ahi,false);
ArraySetAsSeries(g_alo,false); ArraySetAsSeries(g_flag,false);
int start=(prev_calculated>0 ? prev_calculated-1 : 0);
for(int i=start; i<rates_total; i++)
{
long nyc=(long)time[i]+Shift(time[i]);
long nyw=nyc-NY_CLOSE_SHIFT;
long dk=nyc/86400,wk=(dk+3)/7;
int m=(int)((nyw%86400)/60);
g_nyw[i]=(double)nyw;
g_dk[i]=(double)dk;
g_wk[i]=(double)wk;
bool new_day=(i==0 || (long)g_dk[i-1]!=dk);
bool new_week=(i==0 || (long)g_wk[i-1]!=wk);
int fl=0;
//--- day: previous day levels and today's range
if(new_day)
{
g_pdh[i]=(i>0 ? g_dhi[i-1] : EMPTY_VALUE);
g_pdl[i]=(i>0 ? g_dlo[i-1] : EMPTY_VALUE);
g_dhi[i]=high[i];
g_dlo[i]=low[i];
g_dop[i]=open[i];
}
else
{
g_pdh[i]=g_pdh[i-1];
g_pdl[i]=g_pdl[i-1];
g_dhi[i]=MathMax(g_dhi[i-1],high[i]);
g_dlo[i]=MathMin(g_dlo[i-1],low[i]);
g_dop[i]=g_dop[i-1];
fl=(int)g_flag[i-1];
}
//--- week
if(new_week)
{
g_pwh[i]=(i>0 ? g_whi[i-1] : EMPTY_VALUE);
g_pwl[i]=(i>0 ? g_wlo[i-1] : EMPTY_VALUE);
g_whi[i]=high[i];
g_wlo[i]=low[i];
}
else
{
g_pwh[i]=g_pwh[i-1];
g_pwl[i]=g_pwl[i-1];
g_whi[i]=MathMax(g_whi[i-1],high[i]);
g_wlo[i]=MathMin(g_wlo[i-1],low[i]);
}
//--- Asia range of the current trading day
if(i>0)
{
g_aown[i]=g_aown[i-1];
g_adk[i]=g_adk[i-1];
g_ahi[i]=g_ahi[i-1];
g_alo[i]=g_alo[i-1];
}
else
{
g_aown[i]=-1;
g_adk[i]=-1;
g_ahi[i]=0;
g_alo[i]=0;
}
bool in_asia=InSession(m,g_s_start[0],g_s_end[0]);
if(in_asia)
{
long ow=OwnerDate(nyw,m,g_s_start[0],g_s_end[0]);
if((long)g_aown[i]!=ow)
{
g_aown[i]=(double)ow;
g_adk[i]=(double)dk;
g_ahi[i]=high[i];
g_alo[i]=low[i];
}
else
{
g_ahi[i]=MathMax(g_ahi[i],high[i]);
g_alo[i]=MathMin(g_alo[i],low[i]);
}
}
bool asia_ok=((long)g_adk[i]==dk && g_aown[i]!=-1);
g_ash[i]=(asia_ok ? g_ahi[i] : EMPTY_VALUE);
g_asl[i]=(asia_ok ? g_alo[i] : EMPTY_VALUE);
//--- signals: closed bars only, each kind once per trading day
g_sig[i]=EMPTY_VALUE;
if(i<=rates_total-2)
{
int kinds=0;
if(g_pdh[i]!=EMPTY_VALUE)
{
if((fl&1)==0 && close[i]>g_pdh[i])
kinds|=1;
if((fl&2)==0 && close[i]<g_pdl[i])
kinds|=2;
}
if(asia_ok && !in_asia)
{
if((fl&4)==0 && close[i]>g_ahi[i])
kinds|=4;
if((fl&8)==0 && close[i]<g_alo[i])
kinds|=8;
}
fl|=kinds;
if((kinds&1)!=0)
g_sig[i]=1;
else
if((kinds&2)!=0)
g_sig[i]=-1;
else
if((kinds&4)!=0)
g_sig[i]=2;
else
if((kinds&8)!=0)
g_sig[i]=-2;
if(kinds!=0 && i==rates_total-2)
{
string txt="";
if((kinds&1)!=0 && InpAlertPDH)
txt+="closed above previous day high "+DoubleToString(g_pdh[i],_Digits)+"; ";
if((kinds&2)!=0 && InpAlertPDL)
txt+="closed below previous day low "+DoubleToString(g_pdl[i],_Digits)+"; ";
if((kinds&4)!=0 && InpAlertAsiaH)
txt+="closed above Asia high "+DoubleToString(g_ahi[i],_Digits)+"; ";
if((kinds&8)!=0 && InpAlertAsiaL)
txt+="closed below Asia low "+DoubleToString(g_alo[i],_Digits)+"; ";
if(txt!="")
{
CkDbg("alert for the closed bar "+TimeToString(time[i])+": "+StringSubstr(txt,0,StringLen(txt)-2));
SignalAlert(PRODUCT,StringSubstr(txt,0,StringLen(txt)-2),time[i],prev_calculated>0);
}
}
}
g_flag[i]=fl;
}
if(g_no_draw)
return(rates_total);
//--- objects: on a new bar, or (throttled) when the live bar widens the live session
int last=rates_total-1;
bool newbar=(time[last]!=g_draw_bar);
bool widen=(g_secs<=PeriodSeconds(PERIOD_H4) && (high[last]>g_draw_hi || low[last]<g_draw_lo) && GetTickCount()-g_draw_tick>=1000);
if(newbar || widen)
Draw(rates_total,time,high,low);
return(rates_total);
}
//+------------------------------------------------------------------+