# Why is there NO Complete EA within the Code-Base? - page 3

22712

WHRoeder:
Here's mine minus the actual trading logic.
I had a PM concerning the trading time/day filter. The request was 1800-0600 Monday-Friday. That requires excluding 0-0600 Monday and 1800-close on Friday. The code allowed for roll over of time but not for days. Enhancement:
```    /*++++ Day/Time allowed to open*/{
datetime    now = TimeGMT();
//extern double   TradeHr.UTC.Start   =   7.3;    // London-1
//extern double   TradeHr.UTC.End     =  12.9;    // NY open
hrBeg       = (now-secStart+86400)%86400,
hrEnd       = (now-secEnd  +86400)%86400;
if (hrBeg > hrEnd){ double Tminus=hrBeg/3600.-24;
StrApnd(EA.status," HR", DoubleToStr(Tminus,2));    return; }
// TOD:     0600-1800       1800-2359       0000-0600
// DOW:     DOW(today)      DOW(today)      DOW(yesterday)
int         TOD = now % 86400;      // Time of the day
datetime    BOD = now - TOD;        // Beginning of the day (today 0000z)
if (TOD < secStart) int DOW = TimeDayOfWeek(BOD - 1);       // Yesterday.
else                    DOW = TimeDayOfWeek(BOD),   /* https://www.mql5.com/en/forum/127483
// reports DayOfWeek() always returns 5 in the tester. No refresh?*/
int DayMask = 1 << DOW; // #define DAYS_MAX    0x3F// 1<<6-1=63. (S-F)
//extern int      Days.Mask               =  55;      // Not Wed
/*---- Day/Time allowed to open*/}
```
And my correction for RaptorUK's comment:
```double  PointValuePerLot(string pair="") {
/* Value in acnSum currency of a Point of Symbol.
* In tester I had a sale: open=1.35883 close=1.35736 (0.00147)
* gain\$=97.32/6.62 lots/147 points=\$0.10/point or \$1.00/pip.
* IBFX demo/mini       EURUSD TICKVALUE=0.1 MAXLOT=50 LOTSIZE=10,000
* IBFX demo/standard   EURUSD TICKVALUE=1.0 MAXLOT=50 LOTSIZE=100,000
*                                  \$1.00/point or \$10.00/pip.
*
* https://www.mql5.com/en/forum/127584 CB: MODE_TICKSIZE will usually return the
* same value as MODE_POINT (or Point for the current symbol), however, an
* example of where to use MODE_TICKSIZE would be as part of a ratio with
* MODE_TICKVALUE when performing money management calculations which need
* to take acnSum of the pair and the acnSum currency. The reason I use
* this ratio is that although TV and TS may constantly be returned as
* something like 7.00 and 0.00001 respectively, I've seen this
* (intermittently) change to 14.00 and 0.00002 respectively (just example
* tick values to illustrate). */
if (pair == "") pair = Symbol();
return(  MarketInfo(pair, MODE_TICKVALUE)
/ MarketInfo(pair, MODE_TICKSIZE) ); // Not Point.
}
```
22712

WHRoeder:
And my correction for RaptorUK's comment:
I renamed PointValuePerLot to DeltaValuePerLot since aCurrencyChange = aPriceChange * OrderSize() * DeltaValuePerLot(). Also added more comments.
```double  PipValuePerLot(string pair=""){ return(DeltaValuePerLot() * pips2dbl); }
double  DeltaValuePerLot(string pair=""){
/* Value in account currency of a Point of Symbol.
* In tester I had a sale: open=1.35883 close=1.35736 (0.0147)
* gain\$=97.32/6.62 lots/147 points=\$0.10/point or \$1.00/pip.
* IBFX demo/mini       EURUSD TICKVALUE=0.1 MAXLOT=50 LOTSIZE=10,000
* IBFX demo/standard   EURUSD TICKVALUE=1.0 MAXLOT=50 LOTSIZE=100,000
*                                  \$1.00/point or \$10.0/pip.
*
* https://www.mql5.com/en/forum/127584 CB: MODE_TICKSIZE will usually return the
* same value as MODE_POINT (or Point for the current symbol), however, an
* example of where to use MODE_TICKSIZE would be as part of a ratio with
* MODE_TICKVALUE when performing money management calculations which need
* to take account of the pair and the account currency. The reason I use
* this ratio is that although TV and TS may constantly be returned as
* something like 7.00 and 0.0001 respectively, I've seen this
* (intermittently) change to 14.00 and 0.0002 respectively (just example
* tick values to illustrate).
* https://www.mql5.com/en/forum/135345 zzuegg reports for non-currency DE30:
* MarketInfo(Symbol(),MODE_TICKSIZE) returns 0.5
* MarketInfo(Symbol(),MODE_DIGITS) return 1
* Point = 0.1
* Prices to open must be a multiple of ticksize */
if (pair == "") pair = Symbol();
return(  MarketInfo(pair, MODE_TICKVALUE)
/ MarketInfo(pair, MODE_TICKSIZE) ); // Not Point.
}
```
22712

`double  PipValuePerLot(string pair=""){ return(DeltaValuePerLot(pair) * pips2dbl); }`
22712

I modified the Polyline code to allow overlays, e.g.:
```double  bottom      =         WindowPriceMin(),
top         = MathMax(WindowPriceMax(), bottom+pips2dbl),// Div0
topQuarter  = (3*top +   bottom)/4.,
botQuarter  = (top   + 3*bottom)/4.;
int     iVisible    =           WindowFirstVisibleBar(),
iVisEnd     = MathMaxI( iVisible-WindowBarsPerChart(),0);// Shft
static bool drawOnTop = false;
if      (Bid >= topQuarter) drawOnTop = false;  // Hysteresis - previous
else if (Bid <= botQuarter) drawOnTop = true;   // location otherwise.
if (drawOnTop)  bottom  = topQuarter;
else            top     = botQuarter;
for(iWpr = MathMinI(iWprBegin, iVisible); iWpr >= iVisEnd; iWpr--)
Polyline( WPR_NAME, wprValue[iWpr], Color.Wpr, iWpr,
0., 100., bottom, top );
```
Backwards compatible:
```void TLine( string name, datetime T0, double P0, datetime T1, double P1,
color clr, double V0=INF, double V1=INF, bool ray=false){
if (!Show.Objects)  return;                         #define WINDOW_MAIN 0
if      (ObjectMove( name, 0, T0, P0 ))     ObjectMove(name, 1, T1, P1);
else if (!ObjectCreate( name, OBJ_TREND, WINDOW_MAIN, T0, P0, T1, P1 ))
else if (!ObjectSet( name, OBJPROP_RAY, ray ))
Alert("ObjectSet(", name, ",Ray) failed: ", GetLastError());
if (!ObjectSet(name, OBJPROP_COLOR, clr )) // Allow color change
Alert("ObjectSet(", name, ",Color) [2] failed: ", GetLastError());
if (V0 == INF || V0 == P0){
string  P0t = PriceToStr(P0);           if (MathAbs(P0 - P1) >= Point)
P0t = StringConcatenate(P0t, " to ", PriceToStr(P1));       }
else if (V0 == V1)  P0t = StringConcatenate(V0,""); // Suppress trailing to
else                P0t = StringConcatenate(V0, " to ", V1);
if (!ObjectSetText(name, P0t, 10))
}```
22712

```//+------------------------------------------------------------------+
//| EA equivalent of indicator buffers                               |
//+------------------------------------------------------------------+
/*  Example 1:
*  if (...) Ordermodify(...);
*  Polyline("SL"+(oo.ticket%99), oo.SL, Color.SL, 0);
*
*  Example 2:
*  double  ELineCurr = iMA(NULL,0, ELine.Period, 0, MODE_EMA, PRICE_CLOSE, 1);
*  string pln=Polyline("ELine", ELineCurr, Color.ELine, 1);
*      ObjectSet(pln, OBJPROP_STYLE, STYLE_DOT);
*      ObjectSetText(pln, "ELine="+DoubleToStr(ELineCurr,Digits), 10);
******************************************************************************/
#define POLYLINE_MAX 30 // Must match valueXX[]
string  lineName[POLYLINE_MAX]; // Common to Polyline/PolylineDelete and helper.
string  Polyline(string name, double value, color clr, int shift=0,
double indiMin=INF, double indiMax=INF, double chrtMin=INF, double chrtMax=INF){
if (!Show.Objects)  return("");         // Return the actual object name for
static int           LRU[POLYLINE_MAX]; // further modifications, E.G. style
for (int iLine=0; iLine < POLYLINE_MAX; iLine++){
bool isNew = lineName[iLine] != name;   if (!isNew) break;  }
if (isNew){
for (iLine=0; iLine < POLYLINE_MAX; iLine++)  LRU[iLine]++;
iLine=ArrayMaximum(LRU);  lineName[iLine]=name;
}
LRU[iLine] = 0;
double  value00[], value01[], value02[], value03[], value04[],
value05[], value06[], value07[], value08[], value09[],
value10[], value11[], value12[], value13[], value14[],
value15[], value16[], value17[], value18[], value19[],
value20[], value21[], value22[], value23[], value24[],
value25[], value26[], value27[], value28[], value29[];
switch (iLine){
case  0: return(PLHelper(value00, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  1: return(PLHelper(value01, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  2: return(PLHelper(value02, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  3: return(PLHelper(value03, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  4: return(PLHelper(value04, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  5: return(PLHelper(value05, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  6: return(PLHelper(value06, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  7: return(PLHelper(value07, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  8: return(PLHelper(value08, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case  9: return(PLHelper(value09, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 10: return(PLHelper(value10, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 11: return(PLHelper(value11, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 12: return(PLHelper(value12, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 13: return(PLHelper(value13, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 14: return(PLHelper(value14, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 15: return(PLHelper(value15, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 16: return(PLHelper(value16, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 17: return(PLHelper(value17, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 18: return(PLHelper(value18, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 19: return(PLHelper(value19, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 20: return(PLHelper(value20, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 21: return(PLHelper(value21, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 22: return(PLHelper(value22, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 23: return(PLHelper(value23, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 24: return(PLHelper(value24, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 25: return(PLHelper(value25, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 26: return(PLHelper(value26, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 27: return(PLHelper(value27, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 28: return(PLHelper(value28, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
case 29: return(PLHelper(value29, value, clr, iLine, isNew, shift,
indiMin, indiMax, chrtMin, chrtMax));
}
/*NOTREACHED*/
}   // Polyline
string  PLHelper( double& mem[],    double value0,  color clr,      int iLine,
bool isNew,       int shift,      double indiMin,
double indiMax,   double chrtMin, double chrtMax ){
string name = lineName[iLine];
double  price0  = value0;                               if (indiMin < INF){
if (chrtMin == INF) chrtMin = WindowPriceMin();
if (chrtMax == INF) chrtMax = WindowPriceMax();
price0  = (value0 - indiMin) / (indiMax - indiMin)
* (chrtMax - chrtMin) + chrtMin;                        }
datetime    t0  = Time[shift];  static datetime timeL[POLYLINE_MAX];
if (!isNew){
if (timeL[iLine] < Time[shift+1]){      // Missing bar(s), leave a gap.
isNew = true;                                                   }
else if (Time[shift] < timeL[iLine]){   // Redrawing earlier bars.
isNew = true;
for(int iObj=ObjectsTotal()-1; iObj >= 0; iObj--){
string on = ObjectName(iObj);
if (StringFind(on, name) == 0)  ObjectDelete(on);
}   }   }
if (isNew){
if (!ResizeBuffer(mem, 2))  return("");
mem[1]        = price0;     static datetime timeF[POLYLINE_MAX];
timeF[iLine]  = t0;         static color    clrLn[POLYLINE_MAX];
clrLn[iLine]  = clr;        static int      segNo[POLYLINE_MAX];
segNo[iLine]++;                                                     }
else if (clrLn[iLine] != clr){  ArrayResize(mem, 2);    // Series==true;
mem[1]        = mem[0];
timeF[iLine]  = timeL[iLine];
clrLn[iLine]  = clr;
segNo[iLine]++;                                                     }
else if (timeL[iLine] < t0){                    // New bar, remember point
if (!ResizeBuffer(mem, ArraySize(mem)+1))               return(""); }
mem[0]          = price0;   timeL[iLine]    = t0;
int     iFirst  = ArraySize(mem)-1;
double  priceF  = mem[iFirst],
valueF  = priceF;                               if (indiMin < INF){
valueF  = (priceF - chrtMin) / (chrtMax - chrtMin)
* (indiMax - indiMin) + indiMin;                        }
datetime Tf = timeF[iLine];                                 // One bar wide
if (t0 == Tf)   Tf += IfI(-1, +1, shift==0)*60*Period();    // to be visual
string  objName = name+"_"+RJust(segNo[iLine],3);
TLine(objName, Tf, priceF, t0, price0, clr, valueF, value0);
double maxError=0;  for (int iMem=1; iMem < iFirst; iMem++){
double  hight   = ObjectGetValueByShift(objName, iMem+shift),
error   = MathAbs(hight - mem[iMem]);
if (error > maxError){  maxError = error;   int iMaxError = iMem; }
}
if (maxError >= pips2dbl){  // Split the line into two segments at max.
double  priceM  = mem[iMaxError],
valueM  = priceM;                           if (indiMin < INF){
valueM  = (priceM - chrtMin) / (chrtMax - chrtMin)
* (indiMax - indiMin) + indiMin;    }
TLine(objName,  timeF[iLine],           priceF,
Time[iMaxError+shift],  priceM, clr, valueF, valueM);
ArrayResize(mem, iMaxError+1);          // Drop iFirst..(iMaxError+1)
timeF[iLine] = Time[iMaxError+shift];
segNo[iLine]++; objName=name+"_"+RJust(segNo[iLine], 3);
TLine(objName, timeF[iLine], priceM, t0, price0, clr, valueM, value0);
}   // Split the line into two segments at the max.
return(objName);
}   // PLHelper
```
22712

WHRoeder:
I modified the Polyline code to allow overlays, ..
TLne Backwards compatible:
From a post about indicator drawing on both main chart and sub chart, TLine could be used for both with modifications.
```void TLine(string name, datetime T0, double P0, datetime T1, double P1,
color clr, double V0=INF, double V1=INF, bool ray=false, int iWin=0){
if (!Show.Objects)  return;                         #define WINDOW_MAIN 0
if      (ObjectMove( name, 0, T0, P0 ))     ObjectMove(name, 1, T1, P1);
else if (!ObjectCreate( name, OBJ_TREND, iWin, T0, P0, T1, P1 ))
else if (!ObjectSet( name, OBJPROP_RAY, ray ))
Alert("ObjectSet(", name, ",Ray) failed: ", GetLastError());
if (!ObjectSet(name, OBJPROP_COLOR, clr )) // Allow color change
Alert("ObjectSet(", name, ",Color) [4] failed: ", GetLastError());
if (V0 != INF)  // V0, V1 is used for non-price indicator overlayed on chart
if (MathAbs( (V0 - V1) / MathMax(V0, V1) ) < 0.001)             string
P0t = StringConcatenate(V0,"");
else    P0t = StringConcatenate(V0, " to ", V1);
else if (iWin != WINDOW_MAIN)
if (MathAbs( (P0 - P1) / MathMax(P0, P1) ) < 0.001)
P0t = StringConcatenate(P0,"");
else    P0t = StringConcatenate(P0, " to ", P1);
else if (MathAbs(P0 - P1) < Point_2)
P0t = PriceToStr(P0);
else        P0t = StringConcatenate(PriceToStr(P0), " to ", PriceToStr(P1));
if (!ObjectSetText(name, P0t, 10))
}
```
To draw on the sub chart just use iWin = WindowFind("indicatorName");
6722

WHRoeder:
From a post about indicator drawing on both main chart and sub chart, TLine could be used for both with modifications.
To draw on the sub chart just use iWin = WindowFind("indicatorName");

Thanks
22712

### Market close time.

I wanted to close all orders before end of the week (market close Friday,) to prevent loss should the market gap over the week end, by passing the SL. (This is not modeled in the tester.)

So the question is when does the market close (or open.) All of the postings and searching on the net are plain wrong. From Forex Education - Introduction to Forex - IBFX Forex market operates 24 hours a day, 5.5 days a week (6:00 PM ET on Sunday until 4:00 PM ET on Friday) That's ET - New York local time.

This means that All postings depends on when they were made, as DST boundary's varies from year to year.

This means that it is necessary to compute when NY DST starts and ends, for the bar in question. (For back testing we need values to 2000 at least.) The problem is that Windows does not provide conversion routines except between the current TZ and UTC and only for the current year. If the PC isn't on ET it can't be done directly. Further more, until Windows8 comes out, you can't get conversions for ET and prior years. And looking at the Registry on Win7 the values only cover 2005 on (and those were wrong if I read them correctly.) So I'm not betting Win8 will be better.

This means that it is necessary to compute DST myself. I've updated my code: TimeGmt() and LocalTimeGMT() with default arguments, some additional documentation, and checks, and wrote code to compute market close time. Enjoy.

```#define HR2400          86400                   // 24 * 3600
datetime TimeOfDay(datetime when){  return( when % HR2400          );       }
datetime DateOfDay(datetime when){  return( when - TimeOfDay(when) );       }
datetime    now.srv,    now.utc,    mkt.closes.srv;
:
now.srv = TimeCurrent();
now.utc = TimeGMT(now.srv);                 // May update Srvr.To.UTC.Hours
if (now.srv > mkt.closes.srv){              // Compute next market close
// Market closes Friday 4PM ET (NY local time) and opens Sunday 6PM ET.
// That's either 2100z or 2000z depending on DST.
// 18:00 Friday   in America/New_York
// 12:00 Friday   in Pacific/Honolulu
// 08:00 Saturday in Australia/Melbourne
// 07:00 Saturday in Asia/Tokyo
#define DOW_FRIDAY  5
#define HR2100      75600   // 21 * 3600
#define NY_TZ      -18000   // -5 * 3600 UTC-5 (STD) or UTC-4 (DST)
datetime mktClosesUTC   = DateOfDay(now.utc)
+ HR2400*(DOW_FRIDAY - TimeDayOfWeek(now.utc))
+ HR2100;
if (IsNYonDST(mktClosesUTC + NY_TZ))    mktClosesUTC -= 3600;   // UTC-4
mkt.closes.srv = TimeServer(mktClosesUTC);
}                                           // Compute next market close
:
//+------------------------------------------------------------------+
//| GMT Time                                                         |
//+------------------------------------------------------------------+
datetime TimeGMT(datetime serverTime=0){            // Server time to GMT time.
if (serverTime == 0)    serverTime = TimeCurrent();
static datetime nextAutoUpdate;     if (serverTime >= nextAutoUpdate){
nextAutoUpdate = serverTime + 7200;
if (Srvr.To.UTC.Auto
)if(IsDllsAllowed()){                               // Complained @ init
int     srvrToUTC       = LocalTimeGMT() - TimeCurrent();
double  nearestHalfHour = MathRound(srvrToUTC / 1800.);
Srvr.To.UTC.Hours       = nearestHalfHour / 2.; // Update external
}   }
return (serverTime + Srvr.To.UTC.Hours*3600.);
}
datetime TimeServer(datetime gmtTime){              // Server time to GMT time.
return (gmtTime - Srvr.To.UTC.Hours*3600.);
}
#import "kernel32.dll"
int  GetTimeZoneInformation(int& TZInfoArray[]);
#import
datetime LocalTimeGMT(){    // TimeLocal to GMT forum.mql4.com/12057#522900
// This is called only by TimeGMT. Check for IsDLLsAllowed() done there.
int TZInfoArray[43];
int tz  = GetTimeZoneInformation(TZInfoArray);
int GMTshift    = TZInfoArray[0];   // GetTimeZoneInformation will return
#define TIME_ZONE_ID_DAYLIGHT 2     // the right Bias even when it returns
#define TIME_ZONE_ID_UNKNOWN  0     // UNKNOWN
if (tz == TIME_ZONE_ID_DAYLIGHT || tz == TIME_ZONE_ID_UNKNOWN)
GMTshift += TZInfoArray[42];
return (TimeLocal() + GMTshift*60);
/*
*  msdn.microsoft.com/en-us/library/windows/desktop/ms724421%28v=vs.85%29.aspx
*  typedef struct _TIME_ZONE_INFORMATION {     typedef struct _SYSTEMTIME {
*  LONG       Bias;              [ 0]  (min)   WORD wYear;         [0]
*  WCHAR      StandardName[32];  [ 1]          WORD wMonth;            Jan=1
*  SYSTEMTIME StandardDate;      [17]          WORD wDayOfWeek;    [1] Sun=1
*  LONG       StandardBias;      [21]          WORD wDay;
*  WCHAR      DaylightName[32];  [22]          WORD wHour;         [2]
*  SYSTEMTIME DaylightDate;      [38]          WORD wMinute;
*  LONG       DaylightBias;      [42]          WORD wSecond;       [3]
*                                    }         WORD wMilliseconds;        }
*/
}
bool IsNYonDST(datetime whenNY){
// Market opens Sunday 6PM ET (NY local time) and closes Friday 4PM ET.
// DST changes at 2AM Sunday, when the market is closed. I don't have to
// worry about the passed time here, only the date.
int year    = TimeYear(     whenNY);                   #define MARCH     3
int mon     = TimeMonth(    whenNY);                   #define APRIL     4
int dom     = TimeDay(      whenNY);                   #define OCTOBER  10
int dow     = TimeDayOfWeek(whenNY);                   #define NOVEMBER 11
// http://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States
//                                      #History_of_DST_in_the_United_States
// 1986-2006    DST the first Sunday in April to the last Sunday in October
// 2007 on              second Sunday March         first Sunday in November
if (year < 2007){ int   monBeg=APRIL,   secndSun=0,  monEnd=OCTOBER;    }
else{                   monBeg=MARCH;   secndSun=7;  monEnd=NOVEMBER;   }
if (mon < monBeg                     )  return(false);  // Early spring.
if (                     mon > monEnd)  return(false);  // Late fall.
if (mon != monBeg){ if (mon != monEnd)  return(true);   // Summer.
if (year < 2007){   // Find last Sun = dom - (dow-SUNDAY) but SUNDAY==0
for (int lastSun = dom-dow; lastSun+7 <= 31; lastSun += 7){}
return (dom < lastSun);                         // DST Before Sun. &
}   }                                                   // not on markt open
// Otherwise first or second Sunday of MonBegin or first Sunday of MonEnd
for (int firstSun = dom + 7-dow; firstSun > 7; firstSun--){}
if (mon == monBeg)  return (firstSun+secndSun <= dom);  // DST On/After
return (dom < firstSun);                                // DST Before Sun.
}
```
22712

WHRoeder: correction.
```    // Otherwise first or second Sunday of MonBegin or first Sunday of MonEnd
//  for (int firstSun = dom + 7-dow; firstSun > 7; firstSun--){}
for (int firstSun = dom + 7-dow; firstSun > 7; firstSun -= 7){}
```
247

These are good points, there are many more things that are a guesswork even with MT4 functions, which not always return the correct value. For example broker and serverinfo on server and trading parameters, like variable spread, ECN or not, SL required or not turns out only from error handling, symbols info, etc.

Many of them are simply basic things which can not be (and should be) easily checked. For example info on opened charts and history data, time info (like GMT, timezone, DST), market open, close info.

Disaster Recovery would be really good, starting with MT4 not shutting down and restarting as it wants to (update), also terminal.exe proper shutdown, which stays frozen in task manager even hours later than the terminal was closed (and thought to be closed properly with close button without any error message).

Some kind of local datacenter support would be also good, it would make work easier instead of limited global variables and opened files. Not to mention storage of ask, bid, (spread) tick data which is missing.

There are many simple missing things which make life much harder together, and is an awful amount of work to handle them correctly if possible. Many external tips nad tricks even hacks are needed for extensive proper operation. I don't blame anyone who publishes a strategy on codebase and not handles all the possible (many of them broker-specific) problems with MT4 or the server - especially when the point is on the strategy not on error handling.

Even very simple things are missing, we cannot handle logfiles which can easily grow hundreds of megabytes or larger when something goes really wrong. They grow until the disk is full and nothing can be done, not even an optinon in the terminal settings on logfile handling.