From b86e8d5c6be955137683b1c5065663b1623cccc7 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sun, 20 Oct 2024 19:04:13 +0330 Subject: [PATCH] Complete and Test Trading Days POI ... --- Classes/x-saherelm.x-chart-objects.class.mq5 | 216 +++ Classes/x-saherelm.x-poi.class.mq5 | 1624 +++++++++++++++++- Indicators/LitMarketSessions.mq5 | 342 ++++ Libraries/x-saherelm.common.lib.mq5 | 98 ++ 4 files changed, 2276 insertions(+), 4 deletions(-) create mode 100644 Classes/x-saherelm.x-chart-objects.class.mq5 create mode 100644 Indicators/LitMarketSessions.mq5 diff --git a/Classes/x-saherelm.x-chart-objects.class.mq5 b/Classes/x-saherelm.x-chart-objects.class.mq5 new file mode 100644 index 00000000..edb4eb52 --- /dev/null +++ b/Classes/x-saherelm.x-chart-objects.class.mq5 @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Class Library +// -------------------------------------- +// Name: XChartObjects +// Description: provides all require Chart Objects ... +// +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +////////////////////////////////////////////////////// +// +// Global Properties ... +#property library +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://www.saherelm.ir" +#property version "1.00" +#property strict + +// +#include "../Libraries/x-saherelm.common.lib.mq5" + +// +#include +#include + +// +// Definitions ... + +enum ENUM_XCHARTOBJECTS +{ + X_TRADE_DAY, +}; + +enum ENUM_X_TRADE_DAY_IDS +{ + X_TRADE_DAY_FROM, + X_TRADE_DAY_TO +}; + +// +// Extensions ... + +/** + * Convert ENUM_X_TRADE_DAY_IDS membr to String ... + * + * @param value: ENUM_X_TRADE_DAY_IDS member ... + * + * @return ( string ) + */ +string ToString(ENUM_X_TRADE_DAY_IDS value) +{ + // + string result = NULL; + + // + switch (value) + { + // + case X_TRADE_DAY_FROM: + result = "XFROM"; + break; + + // + case X_TRADE_DAY_TO: + result = "XTO"; + break; + } + + // + return result; +} + +// +// Implementation ... +class XCTardeDayObject : public CChartObject +{ + // + public: + // + + // + // Constructors ... + void XCTardeDayObject() + { + } + + // + // Deconstructors ... + void ~XCTardeDayObject() + { + } + + /** + * Create XTradeDayChartObject ... + * + * @param chart_id: Long ... + * @param name: String ... + * @param window: Integer ... + * @param time1: DateTime ... + * @param time2: DateTime ... + * + * @return ( bool ) + */ + bool Create( + long chart_id, + const string name, + const int window, + const datetime from, + const datetime to // + ) + { + // + bool result = false; + + // + string mFromVLineName = ToString(X_TRADE_DAY_FROM) + "_" + name; + result = mFromVLine.Create(chart_id, mFromVLineName, window, from); + if (!result) + { + return result; + } + + // + string mToVLineName = ToString(X_TRADE_DAY_TO) + "_" + name; + result = mToVLine.Create(chart_id, mToVLineName, window, to); + if (!result) + { + return result; + } + + // + // result = Attach(chart_id, name, window, 2); + // if (!result) + // { + // return result; + // } + + // + return result; + } + + /** + * Retrieve Object Type ... + * + * @return ( ENUM_XCHARTOBJECTS ) + */ + virtual ENUM_XCHARTOBJECTS Type() + { + return X_TRADE_DAY; + } + + /** + * Set From Date Line Color ... + * + * @param value: Color ... + * + * @return ( bool ) + */ + bool FromColor(color value) + { + return mFromVLine.Color(value); + } + + /** + * Set From Date Line Style ... + * + * @param value: ENUM_LINE_STYLE ... + * + * @return ( bool ) + */ + bool FromStyle(ENUM_LINE_STYLE value) + { + return mFromVLine.Style(value); + } + + /** + * Set To Date Line Color ... + * + * @param value: Color ... + * + * @return ( bool ) + */ + bool ToColor(color value) + { + return mToVLine.Color(value); + } + + /** + * Set To Date Line Style ... + * + * @param value: ENUM_LINE_STYLE ... + * + * @return ( bool ) + */ + bool ToStyle(ENUM_LINE_STYLE value) + { + return mToVLine.Style(value); + } + + // + protected: + // + + // + private: + // + CChartObjectVLine mFromVLine; + CChartObjectVLine mToVLine; + + // +}; + +// diff --git a/Classes/x-saherelm.x-poi.class.mq5 b/Classes/x-saherelm.x-poi.class.mq5 index ec9f8c22..f426b1ff 100644 --- a/Classes/x-saherelm.x-poi.class.mq5 +++ b/Classes/x-saherelm.x-poi.class.mq5 @@ -24,10 +24,14 @@ // Imports ... // +#include "../Classes/x-saherelm.x-chart-objects.class.mq5" #include "../Classes/x-saherelm.xalert.class.mq5" + +// #include #include #include +#include // // Definitions ... @@ -56,6 +60,14 @@ enum ENUM_XPOIS // Event Types ... enum ENUM_XPOI_EVENTS { + // + // Trading Days ... + X_TRADE_DAY_FIRST_BAR, // Trading Day Start ... + X_TRADE_DAY_LAST_BAR, // Trading Day End ... + // + // Session ... + X_SESSION_OPEN, // Session Open ... + X_SESSION_CLOsSE, // Session Close ... // // Momentum Bar ... X_MOMENTUM_BAR_DETECTED, // Momentum Bar ... @@ -72,12 +84,352 @@ enum ENUM_XPOI_EVENTS X_RESISTANCE_DETECTED, }; +// +// Forext Sessions ... +enum ENUM_XSESSION +{ + X_TOKYO, + X_SYDNEY, + X_LONDON, + X_NEW_YORK +}; + +// +// Represent a Session Working Time ... +struct XSession +{ + // + // Props ... + ENUM_XSESSION session; // Session Identifier + string start; // Start Time + string end; // End Time + int offset; // Time Offset + + // + color startColor; + color endColor; + bool allowDraw; + bool drawLabel; + + /** + * Constructor ... + */ + void XSession() + { + Clean(); + } + + /** + * Clean Structure ... + */ + void Clean() + { + // + start = NULL; + end = NULL; + + // + startColor = clrAqua; + endColor = clrAqua; + allowDraw = false; + drawLabel = false; + } + + /** + * Validate Structure ... + * + * @return ( bool ) + */ + bool IsValid() + { + // + bool result = false; + + // + result = IsValid(start) && + IsValid(end) && + offset >= 0; + + // + return result; + } + + /** + * Initialize Specified Session ... + * + * @param _session: ENUM_XSESSION member ... + * @param _start: String (Session Start Time) ... + * @param _end: String (Session End Time) ... + * + * @return ( bool ) + */ + bool Init( + ENUM_XSESSION _session, + string _start, + string _end, + int _offset // + ) + { + // + bool result = false; + + // + result = IsValid(_start) && + IsValid(_end) && + offset >= 0; + if (!result) + { + return result; + } + + // + session = _session; + offset = _offset; + start = _start; + end = _end; + + // + result = IsValid(); + + // + return result; + } + + /** + * Retrieve Session Start Time ... + * + * @return ( datetime ) + */ + datetime Start() + { + // + datetime result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = (datetime)((int)StringToTime(start) + offset); + + // + return result; + } + + /** + * Retrieve Session End Time ... + * + * @return ( datetime ) + */ + datetime End() + { + // + datetime result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = (datetime)((int)StringToTime(end) + offset); + + // + return result; + } + + /** + * Check Currently Session is Open or not ... + * + * @return ( bool ) + */ + bool IsOpen() + { + // + bool result = false; + + // + result = IsValid(); + if (!result) + { + return result; + } + + // + datetime sTime = Start(); + datetime eTime = End(); + result = IsValid(sTime) && + IsValid(eTime); + if (!result) + { + return result; + } + + // + datetime cTime = TimeCurrent(); + + // + MqlDateTime cTimeStruct; + result = TimeToStruct(cTime, cTimeStruct); + if (!result) + { + return result; + } + + // + ENUM_DAY_OF_WEEK dayOfWeek = (ENUM_DAY_OF_WEEK)cTimeStruct.day_of_week; + + // + result = cTime >= sTime && + cTime < eTime && + (dayOfWeek >= MONDAY && + dayOfWeek <= FRIDAY); + + // + return result; + } + + // +}; + +// +struct XTradeDay +{ + // + datetime from; + datetime to; + + // + void XTradeDay() + { + Clean(); + } + + // + void Clean() + { + // + from = NULL; + to = NULL; + } + + // + bool IsValid() + { + // + bool result = false; + + // + result = IsValid(from) && + IsValid(to); + + // + return result; + } + + // + string GetTag() + { + // + string result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = "XDay_" + ToFormatString(from) + "_" + ToFormatString(to); + + // + return result; + } + + // +}; + +// +// Detected Session ... +struct XMarketSession +{ + // + ENUM_XSESSION session; + datetime from; + datetime to; + + // + void XMarketSession() + { + Clean(); + } + + // + void Clean() + { + from = NULL; + to = NULL; + } + + // + bool IsValid() + { + // + bool result = false; + + // + result = IsValid(from) && + IsValid(to); + + // + return result; + } + + // + string GetTage() + { + // + string result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = + ToString(session) + "_" + + ToFormatString(from) + "_" + + ToFormatString(to) + "_"; + + // + return result; + } +}; + // // XOHCL Event Handler ... typedef void (*TXOHCLEventHandler)( XOHCL &bar // ); +// +// POI Event Handler ... +typedef void (*TXPOIEventHandler)( + ENUM_XPOI_EVENTS event // +); + +// +// Session Event Handler ... +typedef void (*TXSessionEventHandler)( + ENUM_XSESSION session, + ENUM_XPOI_EVENTS event // +); + // // Extensions ... @@ -131,6 +483,46 @@ string ToString(ENUM_XPOIS value) return result; } +/** + * Converts ENUM_XSESSION value to String Representation ... + * + * @param value: ENUM_XSESSION member ... + * + * @return ( string ) + */ +string ToString(ENUM_XSESSION value) +{ + // + string result = NULL; + + // + switch (value) + { + // + case X_SYDNEY: + result = "SydneyY"; + break; + + // + case X_TOKYO: + result = "Tokyo"; + break; + + // + case X_LONDON: + result = "London"; + break; + + // + case X_NEW_YORK: + result = "New York"; + break; + } + + // + return result; +} + // // Implementation ... @@ -178,6 +570,8 @@ class XCPOI : public XSCBaseAlert // DestroyPOIs(); DestroyObjects(); + DestroySessions(); + DestroyEventListeners(); } // @@ -306,6 +700,199 @@ class XCPOI : public XSCBaseAlert // // POI(s) Configs ... + // + // Trading Days ... + + /** + * Get Draw Trading Days State ... + * + * @return ( bool ) + */ + bool AllowDrawTradingDays() + { + return mAllowDrawTradingDays; + } + + /** + * Set Draw Trading Days State ... + * + * @param value: Boolean ... + */ + void AllowDrawTradingDays(bool value) + { + mAllowDrawTradingDays = value; + } + + /** + * Get Trading Days Start Color ... + * + * @return ( color ) + */ + color TradingDaysStartColor() + { + return mTradingDaysStartColor; + } + + /** + * Set Trading Days Start Color ... + * + * @param value: Argument 1 + */ + void TradingDaysStartColor(color value) + { + mTradingDaysStartColor = value; + } + + /** + * Get Trading Days Start Style ... + * + * @return ( ENUM_LINE_STYLE ) + */ + ENUM_LINE_STYLE TradingDaysStartStyle() + { + return mTradingDaysStartStyle; + } + + /** + * Set Trading Days Start Style ... + * + * @param value: ENUM_LINE_STYLE member ... + */ + void TradingDaysStartStyle(ENUM_LINE_STYLE value) + { + mTradingDaysStartStyle = value; + } + + /** + * Get Trading Days End Color ... + * + * @return ( color ) + */ + color TradingDaysEndColor() + { + return mTradingDaysEndColor; + } + + /** + * Set Trading Days End Color ... + * + * @param value: Argument 1 + */ + void TradingDaysEndColor(color value) + { + mTradingDaysEndColor = value; + } + + /** + * Get Trading Days End Style ... + * + * @return ( ENUM_LINE_STYLE ) + */ + ENUM_LINE_STYLE TradingDaysEndStyle() + { + return mTradingDaysEndStyle; + } + + /** + * Set Trading Days End Style ... + * + * @param value: ENUM_LINE_STYLE member ... + */ + void TradingDaysEndStyle(ENUM_LINE_STYLE value) + { + mTradingDaysEndStyle = value; + } + + // + // Sessions ... + + /** + * Get Session Label Font Size ... + * + * @return ( int ) + */ + int SessionFontSize() + { + return mSessionFontSize; + } + + /** + * Set Session Label Font Size ... + * + * @param value: Integer ... + */ + void SessionFontSize(int value) + { + // + if (value < 2) + { + value = 2; + } + + // + if (value > 30) + { + value = 30; + } + + // + mSessionFontSize = value; + } + + /** + * Get Session Label Font ... + * + * @return ( string ) + */ + string SessionFont() + { + return mSessionFont; + } + + /** + * Set Session Label Font ... + * + * @param value: String ... + */ + void SessionFont(string value) + { + // + if (!IsValid(value)) + { + value = "Arial"; + } + + // + mSessionFont = value; + } + + /** + * Get Session Label Angle ... + * + * @return ( double ) + */ + double SessionAngle() + { + return mSessionAngle; + } + + /** + * Set Session Label Angle ... + * + * @param value: Double ... + */ + void SessionAngle(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mSessionAngle = value; + } + // // Swing High ... @@ -744,6 +1331,189 @@ class XCPOI : public XSCBaseAlert mMomentumBarBearishColor = value; } + // + // Rejection Bars ... + + /** + * Get Draw Rejection Bars State ... + * + * @return ( bool ) + */ + bool AllowDrawRejectionBars() + { + return mAllowDrawRejectionBars; + } + + /** + * Set Draw Rejection Bars State ... + * + * @param value: Boolean ... + */ + void AllowDrawRejectionBars(bool value) + { + mAllowDrawRejectionBars = value; + } + + /** + * Get Rejection Bar Percent ... + * + * @return ( int ) + */ + int RejectionBarPercent() + { + return mRejectionBarPercent; + } + + /** + * Set Rejection Bar Percent ... + * + * @param value: Integer + */ + void RejectionBarPercent(int value) + { + mRejectionBarPercent = value; + } + + /** + * Get Rejection Bar Sharp Ratio ... + * + * @return ( double ) + */ + double RejectionBarSharpRatio() + { + return mRejectionBarSharpRatio; + } + + /** + * Set Rejection Bar Sharp Ratio ... + * + * @param value: Double ... + */ + void RejectionBarSharpRatio(double value) + { + mRejectionBarSharpRatio = value; + } + + /** + * Get Rejection Bar Loopback ... + * + * @return ( int ) + */ + int RejectionBarLoopBack() + { + return mRejectionBarLoopBack; + } + + /** + * Set Rejection Bar Loopback ... + * + * @param value: Integer ... + */ + void RejectionBarLoopBack(int value) + { + mRejectionBarLoopBack = value; + } + + /** + * Get Rejection Bar Width ... + * + * @return ( int ) + */ + int RejectionBarWidth() + { + return mRejectionBarWidth; + } + + /** + * Set Rejection Bar Width ... + * + * @param value: Integer ... + */ + void RejectionBarWidth(int value) + { + mRejectionBarWidth = value; + } + + /** + * Get Bullish Rejection Bar Arrow Code ... + * + * @return ( char ) + */ + char RejectionBarBullishArrowCode() + { + return mRejectionBarBullishArrowCode; + } + + /** + * Set Bullish Rejection Bar Arrow Code ... + * + * @param value: Char ... + */ + void RejectionBarBullishArrowCode(char value) + { + mRejectionBarBullishArrowCode = value; + } + + /** + * Get Bullish Rejection Bar Color ... + * + * @return ( char ) + */ + char RejectionBarBearishArrowCode() + { + return mRejectionBarBearishArrowCode; + } + + /** + * Set Bullish Rejection Bar Color ... + * + * @param value: Color ... + */ + void RejectionBarBearishArrowCode(char value) + { + mRejectionBarBearishArrowCode = value; + } + + /** + * Get Bearish Rejection Bar Arrow Code ... + * + * @return ( color ) + */ + color RejectionBarBullishColor() + { + return mRejectionBarBullishColor; + } + + /** + * Set Bearish Rejection Bar Arrow Code ... + * + * @param value: Color ... + */ + void RejectionBarBullishColor(color value) + { + mRejectionBarBullishColor = value; + } + + /** + * Get Bearish Rejection Bar Color ... + * + * @return ( color ) + */ + color RejectionBarBearishColor() + { + return mRejectionBarBearishColor; + } + + /** + * Set Bearish Rejection Bar Color ... + * + * @param value: Color ... + */ + void RejectionBarBearishColor(color value) + { + mRejectionBarBearishColor = value; + } + // // Actions ... @@ -780,12 +1550,28 @@ class XCPOI : public XSCBaseAlert } // - int cIndex = 1; + int zIndex = 0; + int cIndex = zIndex + 1; int pIndex = cIndex + 1; + // + datetime cTime = TimeCurrent(); + + // + XOHCL zBar; + bool isInited = zBar.Init( + mSymbol, + mPeriod, + zIndex // + ); + if (!isInited) + { + return result; + } + // XOHCL cBar; - bool isInited = cBar.Init( + isInited = cBar.Init( mSymbol, mPeriod, cIndex // @@ -807,6 +1593,70 @@ class XCPOI : public XSCBaseAlert return result; } + // + XOHCL zDayBar; + isInited = zDayBar.Init( + mSymbol, + PERIOD_D1, + zIndex // + ); + if (!isInited) + { + return result; + } + + // + // Trading Days ... + int tradingDaysCount = ArraySize(mTradingDays); + int tradingDayIDX = HasTradeDay(zDayBar.time); + if (IsValidIndex(tradingDayIDX)) + { + // + if (zBar.time == mTradingDays[tradingDayIDX].to) + { + // + Add( + X_TRADE_DAY_LAST_BAR, + state // + ); + OnTradingDayEvent(X_TRADE_DAY_LAST_BAR); + NotifyTradingDayEventListeners(X_TRADE_DAY_LAST_BAR); + } + } + else + { + // + // there isn't any Trading Day ... + // create one ... + + // + XTradeDay tradeDay; + tradeDay.from = zDayBar.time; + tradeDay.to = zDayBar.GetLastBarTimeOfPeriod(mPeriod); + + // + AddRef( + tradeDay, + mTradingDays // + ); + + // + DrawTradingDays(); + + // + // Notfy Day Started ... + if (zBar.time == zDayBar.time) + { + // + Add( + X_TRADE_DAY_FIRST_BAR, + state // + ); + OnTradingDayEvent(X_TRADE_DAY_FIRST_BAR); + NotifyTradingDayEventListeners(X_TRADE_DAY_FIRST_BAR); + } + } + // // Detect Swing High ... int swingHighsCount = ArraySize(mSwingHighs); @@ -913,9 +1763,25 @@ class XCPOI : public XSCBaseAlert bool state = true; // + AllowDrawTradingDays(state); AllowDrawSwingHighs(state); AllowDrawSwingLows(state); AllowDrawMomentumBars(state); + AllowDrawRejectionBars(state); + + // + // Sessions ... + int sessionsCount = ArraySize(mSessions); + if (IsValidSize(sessionsCount)) + { + // + for (int i = 0; i < sessionsCount; i++) + { + // + mSessions[i].allowDraw = state; + mSessions[i].drawLabel = state; + } + } } /** @@ -927,9 +1793,25 @@ class XCPOI : public XSCBaseAlert bool state = false; // + AllowDrawTradingDays(state); AllowDrawSwingHighs(state); AllowDrawSwingLows(state); AllowDrawMomentumBars(state); + AllowDrawRejectionBars(state); + + // + // Sessions ... + int sessionsCount = ArraySize(mSessions); + if (IsValidSize(sessionsCount)) + { + // + for (int i = 0; i < sessionsCount; i++) + { + // + mSessions[i].allowDraw = state; + mSessions[i].drawLabel = state; + } + } } // @@ -944,6 +1826,92 @@ class XCPOI : public XSCBaseAlert ChartID(0); Window(0); + // + // Trading Days ... + TradingDaysStartColor(clrYellow); + TradingDaysStartStyle(STYLE_DASHDOT); + TradingDaysEndColor(clrGray); + TradingDaysEndStyle(STYLE_DASHDOT); + + // + // Sessions ... + + // + int offset = -10800; + + // + // SYDNEY ... + // Start: (UTC) 21:00 + // End: (UTC) 06:00 + XSession mSyndeySession; + mSyndeySession.Init( + X_SYDNEY, + "21:00", // Start ... + "06:00", // End ... + offset // + ); + mSyndeySession.startColor = clrFireBrick; + mSyndeySession.endColor = clrFireBrick; + mSyndeySession.allowDraw = true; + mSyndeySession.drawLabel = true; + AddSession(mSyndeySession); + + // + // TOKYO ... + // Start: (UTC) 12:00 + // End: (UTC) 09:00 + XSession mTokyoSession; + mTokyoSession.Init( + X_TOKYO, + "12:00", // Start ... + "09:00", // End ... + offset // + ); + mTokyoSession.startColor = clrBlueViolet; + mTokyoSession.endColor = clrBlueViolet; + mTokyoSession.allowDraw = true; + mTokyoSession.drawLabel = true; + AddSession(mTokyoSession); + + // + // LONDON ... + // Start: (UTC) 07:00 + // End: (UTC) 16:00 + XSession mLondonSession; + mLondonSession.Init( + X_LONDON, + "07:00", // Start ... + "16:00", // End ... + offset // + ); + mLondonSession.startColor = clrLime; + mLondonSession.endColor = clrLime; + mLondonSession.allowDraw = true; + mLondonSession.drawLabel = true; + AddSession(mLondonSession); + + // + // NEWYORK ... + // Start: (UTC) 13:00 + // End: (UTC) 22:00 + XSession mNewYorkSession; + mNewYorkSession.Init( + X_LONDON, + "13:00", // Start ... + "22:00", // End ... + offset // + ); + mNewYorkSession.startColor = clrAqua; + mNewYorkSession.endColor = clrAqua; + mNewYorkSession.allowDraw = true; + mNewYorkSession.drawLabel = true; + AddSession(mNewYorkSession); + + // + SessionFont("Arial"); + SessionFontSize(16); + SessionAngle(90); + // MaxNumberOfPOIs(10); MaxNumberOfRequiredPOIs(2); @@ -969,11 +1937,98 @@ class XCPOI : public XSCBaseAlert MomentumBarBearishArrowCode((char)226); MomentumBarBullishColor(clrAqua); MomentumBarBearishColor(clrMagenta); + + // + RejectionBarPercent(55); + RejectionBarLoopBack(5); + RejectionBarSharpRatio(1.5); + RejectionBarWidth(5); + RejectionBarBullishArrowCode((char)217); + RejectionBarBearishArrowCode((char)218); + RejectionBarBullishColor(clrAqua); + RejectionBarBearishColor(clrMagenta); + + // + } + + // + // Session Configurations ... + + /** + * Add Sepcific Session to Supported Sessions ... + * + * @param session: XSession instance ... + */ + void AddSession(XSession &session) + { + // + if (!session.IsValid()) + { + return; + } + + // + AddRef( + session, + mSessions // + ); } // // Event Listeners ... + /** + * Calls When new Trading Day Event Happens ... + * + * @param event: ENUM_XPOI_EVENTS member ... + */ + virtual void OnTradingDayEvent( + ENUM_XPOI_EVENTS event // + ) + { + } + + /** + * Register an Event Listener for New Trading Day Events ... + * + * @param listener: TXPOIEventHandler instance ... + */ + void AddTradingDayEventListener(TXPOIEventHandler listener) + { + // + Add( + listener, + mTradingDayEventListeners // + ); + } + + /** + * Calls When new Market Session Event Happens ... + * + * @param session: ENUM_XSESSION member ... + * @param event: ENUM_XPOI_EVENTS member ... + */ + virtual void OnMarketSessionEvent( + ENUM_XSESSION session, + ENUM_XPOI_EVENTS event // + ) + { + } + + /** + * Register an Event Listener for New Market Session Events ... + * + * @param listener: TXSessionEventHandler instance ... + */ + void AddMarketSessionEventListener(TXSessionEventHandler listener) + { + // + Add( + listener, + mMarketSessionEventListeners // + ); + } + /** * Calls When new Swing High Detected ... * @@ -1043,6 +2098,29 @@ class XCPOI : public XSCBaseAlert ); } + /** + * Calls When new Rejection Bar Detected ... + * + * @param bar: XOHCL instance ... + */ + virtual void OnRejectionBarDetected(XOHCL &bar) + { + } + + /** + * Register an Event Listener for New Rejection Bar Detected ... + * + * @param listener: XOHCL instance ... + */ + void AddRejectionBarEventListener(TXOHCLEventHandler listener) + { + // + Add( + listener, + mRejectionBarEventListeners // + ); + } + // protected: // @@ -1097,9 +2175,44 @@ class XCPOI : public XSCBaseAlert int zIndex = barIndex; int cIndex = zIndex + 1; + // + datetime cTime = TimeCurrent(); + + // + XOHCL zBar; + bool isRetrieved = mBarTracker.GetBar(zBar, zIndex); + if (!isRetrieved) + { + return; + } + // XOHCL cBar; - bool isRetrieved = mBarTracker.GetBar(cBar, cIndex); + isRetrieved = mBarTracker.GetBar(cBar, cIndex); + if (!isRetrieved) + { + return; + } + + // + XOHCL zDayBar; + isRetrieved = zDayBar.Init( + mSymbol, + PERIOD_D1, + zIndex // + ); + if (!isRetrieved) + { + return; + } + + // + XOHCL cDayBar; + isRetrieved = cDayBar.Init( + mSymbol, + PERIOD_D1, + cIndex // + ); if (!isRetrieved) { return; @@ -1108,6 +2221,37 @@ class XCPOI : public XSCBaseAlert // // Detect POI(s) based On Current Bar ... + // + // XTradeDays ... + int daysCount = ArraySize(mTradingDays); + if (daysCount < requiredPOIs) + { + // + XTradeDay tradeDay; + tradeDay.from = zDayBar.time; + + // + datetime lastBarTime = zDayBar.GetLastBarTimeOfPeriod(mPeriod); + tradeDay.to = lastBarTime; + + // + int idx = HasTradeDay(tradeDay.from); + if (!IsValidIndex(idx)) + { + // + AddRef( + tradeDay, + mTradingDays // + ); + + // + daysCount = ArraySize(mTradingDays); + } + } + + // + // Session ... + // // Swing Highs ... int swingHighsCount = ArraySize(mSwingHighs); @@ -1188,11 +2332,22 @@ class XCPOI : public XSCBaseAlert } } + // + // Rejection Bars ... + int rejectionBarsCount = ArraySize(mRejectionBars); + if (rejectionBarsCount < requiredPOIs) + { + // + } + // bool canContinue = + // + daysCount < requiredPOIs || // swingHighsCount < requiredPOIs || swingLowsCount < requiredPOIs || + // momentumBarsCount < requiredPOIs // ; @@ -1205,6 +2360,21 @@ class XCPOI : public XSCBaseAlert // // Complete Calculations ... + // + // Reverse Trading Days ... + XTradeDay tmpDays[]; + Copy( + mTradingDays, + tmpDays // + ); + Clean(mTradingDays); + ArrayReverse(tmpDays); + Copy( + tmpDays, + mTradingDays // + ); + Clean(tmpDays); + // XOHCL tmpBars[]; @@ -1264,12 +2434,29 @@ class XCPOI : public XSCBaseAlert */ void DestroyPOIs() { + // + DestroyTradingDays(); + + // + DestroyMarketSessions(); + // DestroySwingHighs(); DestroySwingLows(); // DestroyMomentumBars(); + + // + DestroyRejectionBars(); + } + + /** + * Destroy Trading Days ... + */ + void DestroyTradingDays() + { + Clean(mTradingDays); } /** @@ -1296,6 +2483,22 @@ class XCPOI : public XSCBaseAlert Clean(mMomentumBars); } + /** + * Destroy Rejection Bars ... + */ + void DestroyRejectionBars() + { + Clean(mRejectionBars); + } + + /** + * Destroy Market Sessions ... + */ + void DestroyMarketSessions() + { + Clean(mMarketSessions); + } + // // Draw Actions ... @@ -1304,12 +2507,62 @@ class XCPOI : public XSCBaseAlert */ void DrawPOIs() { + // + DrawTradingDays(); + + // + DrawMarketSessions(); + // DrawSwingHighs(); DrawSwingLows(); // DrawMomentumBars(); + + // + DrawRejectionBars(); + } + + /** + * Draw Trading Days ... + */ + void DrawTradingDays() + { + // + bool allow = AllowDrawTradingDays(); + if (!allow) + { + return; + } + + // + int count = ArraySize(mTradingDays); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + XTradeDay iDay = mTradingDays[i]; + + // + string iName = iDay.GetTag(); + int iIDX = HasObject( + iName, + mTradingDayObjects // + ); + if (IsValidIndex(iIDX)) + { + continue; + } + + // + CreateTradingDayObject(iDay, iName); + } } /** @@ -1435,15 +2688,46 @@ class XCPOI : public XSCBaseAlert } } + /** + * Draw Rejection Bars ... + */ + void DrawRejectionBars() + { + // + // TODO: Implement Rejection Bars ... + } + + /** + * Draw Market Sessions ... + */ + void DrawMarketSessions() + { + // + // TODO: Implement Market Sessions ... + } + + // + /** * Clean All Drawn Objects ... */ void DestroyObjects() { // - DestroySwingHighObjects(); DestroySwingLowObjects(); + DestroySwingHighObjects(); + DestroyTradingTradingDayObjects(); DestroyMomentumBarObjects(); + DestroyRejectionBarObjects(); + DestroyMarketSessionObjects(); + } + + /** + * Clean Trading Day Drawn Objects ... + */ + void DestroyTradingTradingDayObjects() + { + mTradingDayObjects.Clear(); } /** @@ -1470,6 +2754,25 @@ class XCPOI : public XSCBaseAlert mMomentumBarObjects.Clear(); } + /** + * Clean Rejection Bars Drawn Objects ... + */ + void DestroyRejectionBarObjects() + { + mRejectionBarObjects.Clear(); + } + + /** + * Destroy Market Sessions Drawn Objects ... + */ + void DestroyMarketSessionObjects() + { + // + // TODO: Implement this ... + } + + // + /** * Detect an Object Exists in Array or not ... * @@ -1521,6 +2824,58 @@ class XCPOI : public XSCBaseAlert // // Event Handler Notifiers ... + /** + * Notify New Trading Day Event Handlers ... + * + * @param event: ENUM_XPOI_EVENTS member ... + */ + void NotifyTradingDayEventListeners( + ENUM_XPOI_EVENTS event // + ) + { + // + int count = ArraySize(mTradingDayEventListeners); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + TXPOIEventHandler iListener = mTradingDayEventListeners[i]; + iListener(event); + } + } + + /** + * Notify New Market Session Event Handlers ... + * + * @param session: ENUM_XSESSION member ... + * @param event: ENUM_XPOI_EVENTS member ... + */ + void NotifyMarketSessionEventListeners( + ENUM_XSESSION session, + ENUM_XPOI_EVENTS event // + ) + { + // + int count = ArraySize(mMarketSessionEventListeners); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + TXSessionEventHandler iListener = mMarketSessionEventListeners[i]; + iListener(session, event); + } + } + /** * Notify New Swing High Point Event Handlers ... * @@ -1608,6 +2963,188 @@ class XCPOI : public XSCBaseAlert } } + /** + * Notify New Rejection Bar Event Handlers ... + * + * @param bar: XOHCL instance ... + */ + void NotifyRejectionBarEventListeners(XOHCL &bar) + { + // + if (!bar.IsValid()) + { + return; + } + + // + int count = ArraySize(mRejectionBarEventListeners); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + TXOHCLEventHandler iListener = mRejectionBarEventListeners[i]; + iListener(bar); + } + } + + /** + * Destroy all Registered Event Listeners ... + */ + void DestroyEventListeners() + { + // + Clean(mSwingLowEventListeners); + Clean(mSwingHighEventListeners); + Clean(mTradingDayEventListeners); + Clean(mMomentumBarEventListeners); + Clean(mRejectionBarEventListeners); + Clean(mMarketSessionEventListeners); + } + + // + // Session ... + + /** + * Find Specific Session Index ... + * + * @param session: ENUM_XSESSION member ... + * + * @return ( int ) + */ + int FindSession(ENUM_XSESSION session) + { + // + int result = -1; + + // + int count = ArraySize(mSessions); + if (!IsValidSize(count)) + { + return result; + } + + // + for (int i = 0; i < count; i++) + { + // + XSession iSession = mSessions[i]; + + // + if (iSession.session == session) + { + // + result = i; + break; + } + } + + // + return result; + } + + /** + * Destroy Sessions ... + */ + void DestroySessions() + { + Clean(mSessions); + } + + // + + /** + * Find Specified Trade Day based on Start Time ... + * + * @param start: DateTime ... + * + * @return ( int ) + */ + int HasTradeDay(datetime start) + { + // + int result = -1; + + // + if (!IsValid(start)) + { + return result; + } + + // + int count = ArraySize(mTradingDays); + if (!IsValidSize(count)) + { + return result; + } + + // + for (int i = 0; i < count; i++) + { + // + XTradeDay iDay = mTradingDays[i]; + + // + if (iDay.from == start) + { + // + result = i; + break; + } + } + + // + return result; + } + + /** + * Find Specified Trade Day based on End Time ... + * + * @param start: DateTime ... + * + * @return ( int ) + */ + int HasTradeDayBayEndTime(datetime end) + { + // + int result = -1; + + // + if (!IsValid(end)) + { + return result; + } + + // + int count = ArraySize(mTradingDays); + if (!IsValidSize(count)) + { + return result; + } + + // + for (int i = 0; i < count; i++) + { + // + XTradeDay iDay = mTradingDays[i]; + + // + if (iDay.to == end) + { + // + result = i; + break; + } + } + + // + return result; + } + // private: // @@ -1627,6 +3164,70 @@ class XCPOI : public XSCBaseAlert // XBarTracker mBarTracker; // Market Bar Tracker ... + // + bool mAllowDrawTradingDays; // Draw Trading Days ... + color mTradingDaysStartColor; // Trading Days Start Color ... + ENUM_LINE_STYLE mTradingDaysStartStyle; // Trading Days Start Style ... + color mTradingDaysEndColor; // Trading Days End Color ... + ENUM_LINE_STYLE mTradingDaysEndStyle; // Trading Days End Style ... + XTradeDay mTradingDays[]; // Trading Days ... + CArrayObj mTradingDayObjects; // Trading Day Chart Objects ... + TXPOIEventHandler mTradingDayEventListeners[]; // Trading Day Event Listeners ... + + /** + * Create an Object to Represent Trading Day on Chart ... + * + * @param day: XTradeDay instance ... + * @param name: String ... + */ + void CreateTradingDayObject( + XTradeDay &day, + string name // + ) + { + // + if (!day.IsValid() || + !IsValid(name)) + { + return; + } + + // + XCTardeDayObject *iObj; + iObj = new XCTardeDayObject(); + bool isCreated = iObj.Create( + ChartID(), + name, + Window(), + day.from, + day.to // + ); + if (!isCreated) + { + return; + } + + // + iObj.FromColor(TradingDaysStartColor()); + iObj.FromStyle(TradingDaysStartStyle()); + + // + iObj.ToColor(TradingDaysEndColor()); + iObj.ToStyle(TradingDaysEndStyle()); + + // + } + + // + // Configure Sessions ... + int mSessionFontSize; // Session Label Font Size ... + string mSessionFont; // Session Label Font ... + double mSessionAngle; // Session Label Angle ... + XSession mSessions[]; // Forex Sessions + XMarketSession mMarketSessions[]; // Founded Market Sessions ... + CArrayObj mMarketSessionObjects; // Founded Market Session Chart Ojects ... + TXSessionEventHandler mMarketSessionEventListeners[]; // Market Sessions Event Listeners ... + // // POI Holders ... @@ -1821,6 +3422,21 @@ class XCPOI : public XSCBaseAlert mMomentumBarObjects.Add(iObj); } + // + // Rejection Bar .... + bool mAllowDrawRejectionBars; // Draw Rejection Bars ... + int mRejectionBarPercent; // Rejection Bar Percent ... + double mRejectionBarSharpRatio; // Rejection Bar Sharp Ratio ... + int mRejectionBarLoopBack; // Rejection Bar Loopback ... + int mRejectionBarWidth; // Rejection Bar Width ... + char mRejectionBarBullishArrowCode; // Bullish Rejection Bar Arrow Code ... + char mRejectionBarBearishArrowCode; // Bullish Rejection Bar Color ... + color mRejectionBarBullishColor; // Bearish Rejection Bar Arrow Code ... + color mRejectionBarBearishColor; // Bearish Rejection Bar Color ... + XOHCL mRejectionBars[]; // Rejection Bars ... + CArrayObj mRejectionBarObjects; // Rejection Bar Chart Objects ... + TXOHCLEventHandler mRejectionBarEventListeners[]; // Rejection Bars Event Listeners ... + // }; diff --git a/Indicators/LitMarketSessions.mq5 b/Indicators/LitMarketSessions.mq5 new file mode 100644 index 00000000..3ab84b11 --- /dev/null +++ b/Indicators/LitMarketSessions.mq5 @@ -0,0 +1,342 @@ +//+------------------------------------------------------------------+ +//| LitMarketSessions.mq5 | +//| Copyright 2024, rpanchyk | +//| https://github.com/rpanchyk | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2024, rpanchyk" +#property link "https://github.com/rpanchyk" +#property version "1.00" +#property description "Indicator shows LIT market sessions" + +#property indicator_chart_window +#property indicator_buffers 3 +#property indicator_plots 1 + +// includes +#include +#include + +enum ENUM_TIME_ZONE + { + TZauto = 99, // auto + TZp6 = 6, // +6 + TZp5 = 5, // +5 + TZp4 = 4, // +4 + TZp3 = 3, // +3 + TZp2 = 2, // +2 + TZp1 = 1, // +1 + TZp0 = 0, // 0 + TZm1 = -1, // -1 + TZm2 = -2, // -2 + TZm3 = -3, // -3 + TZm4 = -4, // -4 + TZm5 = -5, // -5 + TZm6 = -6 // -6 + }; + +enum ENUM_BORDER_STYLE + { + BORDER_STYLE_SOLID = STYLE_SOLID, // Solid + BORDER_STYLE_DASH = STYLE_DASH // Dash + }; + +enum ENUM_LIT_SESSION_TYPE + { + LIT_SESSION_LONDON, // 08 AM to 09 AM [UTC] - Open Inducement Window (1 hour) + LIT_SESSION_NEWYORK, // 01 PM to 02 PM [UTC] - Open Inducement Window (1 hour) + LIT_SESSION_TOKYO // 23 PM to 06 AM [UTC] + }; + +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +class Box : public CObject + { +public: + Box(ENUM_LIT_SESSION_TYPE inType, datetime inStart, datetime inEnd, double inLow, double inHigh) + { + this.type = inType; + this.start = inStart; + this.end = inEnd; + this.low = inLow; + this.high = inHigh; + } + + void draw() + { + string objName = "sbox " + TimeToString(start); + if(ObjectFind(0, objName) < 0) + { + ObjectCreate(0, objName, OBJ_RECTANGLE, 0, start, low, end, high); + + ObjectSetInteger(0, objName, OBJPROP_COLOR, getTypeAsColor()); + ObjectSetInteger(0, objName, OBJPROP_FILL, InpFill); + ObjectSetInteger(0, objName, OBJPROP_STYLE, InpBoderStyle); + ObjectSetInteger(0, objName, OBJPROP_WIDTH, InpBorderWidth); + ObjectSetInteger(0, objName, OBJPROP_BACK, true); + ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); + ObjectSetInteger(0, objName, OBJPROP_HIDDEN, false); + ObjectSetInteger(0, objName, OBJPROP_ZORDER, 0); + } + } + + long getTypeAsColor() + { + switch(type) + { + case LIT_SESSION_LONDON: + return InpLondonColor; + case LIT_SESSION_NEWYORK: + return InpNewyorkColor; + case LIT_SESSION_TOKYO: + return InpTokyoColor; + default: + Print("Unknown type"); + return -1; + } + } + + int getTypeAsNumber() + { + switch(type) + { + case LIT_SESSION_LONDON: + return 1; + case LIT_SESSION_NEWYORK: + return 2; + case LIT_SESSION_TOKYO: + return 3; + default: + Print("Unknown type"); + return -1; + } + } + + ENUM_LIT_SESSION_TYPE type; + datetime start; + datetime end; + double low; + double high; + }; + +// buffers +double TypeBuffer[]; +double LowBuffer[]; +double HighBuffer[]; + +// config +input group "Section :: Main"; +input ENUM_TIME_ZONE InpTimeZoneOffsetHours = TZauto; // Time zone (offset in hours) +input bool InpLondonShow = true; // Show London +input bool InpNewyorkShow = true; // Show NewYork +input bool InpTokyoShow = true; // Show Tokyo +input group "Section :: Style"; +input color InpLondonColor = clrLightGreen; // London color +input color InpNewyorkColor = clrYellow; // NewYork color +input color InpTokyoColor = clrLightGray; // Tokyo color +input bool InpFill = true; // Fill solid (true) or transparent (false) +input ENUM_BORDER_STYLE InpBoderStyle = BORDER_STYLE_SOLID; // Border line style +input int InpBorderWidth = 2; // Border line width + +// runtime +CArrayObj boxes; +int timeShiftSec; + +//+------------------------------------------------------------------+ +//| Custom indicator initialization function | +//+------------------------------------------------------------------+ +int OnInit() + { + Print("Initialization started"); + + ArrayInitialize(TypeBuffer, 0); + ArrayInitialize(LowBuffer, 0); + ArrayInitialize(HighBuffer, 0); + + ArraySetAsSeries(TypeBuffer, true); + ArraySetAsSeries(LowBuffer, true); + ArraySetAsSeries(HighBuffer, true); + + SetIndexBuffer(0, TypeBuffer, INDICATOR_DATA); + SetIndexBuffer(1, LowBuffer, INDICATOR_CALCULATIONS); + SetIndexBuffer(2, HighBuffer, INDICATOR_CALCULATIONS); + + timeShiftSec = (InpTimeZoneOffsetHours == TZauto ? getTimeZoneOffsetHours() : InpTimeZoneOffsetHours) * 60 * 60; + + Print("Initialization finished"); + return INIT_SUCCEEDED; + } + +//+------------------------------------------------------------------+ +//| Custom indicator deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + Print("Deinitialization started"); + + ObjectsDeleteAll(0, "sbox"); + + Print("Deinitialization finished"); + } + +//+------------------------------------------------------------------+ +//| 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[]) + { + if(rates_total == prev_calculated) + { + return rates_total; + } + + ArraySetAsSeries(time, true); + ArraySetAsSeries(high, true); + ArraySetAsSeries(low, true); + + int limit = (int) MathMin(rates_total, rates_total - prev_calculated + 1); +//PrintFormat("RatesTotal: %i, PrevCalculated: %i, Limit: %i", rates_total, prev_calculated, limit); + + MqlDateTime currMdt; + MqlDateTime startMdt; + MqlDateTime endMdt; + + datetime currDt; + datetime startDt; + datetime endDt; + + for(int i = limit - 1; i > 0; i--) + { + datetime dt = time[i]; + //Print(i, " at ", TimeToString(dt), " GMT"); + + TimeToStruct(dt, currMdt); + currDt = StructToTime(currMdt); + + TimeToStruct(dt, startMdt); + startMdt.min = 0; + startMdt.sec = 0; + + TimeToStruct(dt, endMdt); + endMdt.min = 0; + endMdt.sec = 0; + + // London + if(InpLondonShow) + { + startMdt.hour = 8; + endMdt.hour = 9; + startDt = StructToTime(startMdt) + timeShiftSec; + endDt = StructToTime(endMdt) + timeShiftSec; + + if(currDt >= startDt && currDt < endDt) + { + addBox(&boxes, LIT_SESSION_LONDON, startDt, endDt, low[i], high[i], i); + } + } + + // NewYork + if(InpNewyorkShow) + { + startMdt.hour = 13; + endMdt.hour = 14; + startDt = StructToTime(startMdt) + timeShiftSec; + endDt = StructToTime(endMdt) + timeShiftSec; + + if(currDt >= startDt && currDt < endDt) + { + addBox(&boxes, LIT_SESSION_NEWYORK, startDt, endDt, low[i], high[i], i); + } + } + + // Tokyo + if(InpTokyoShow) + { + startMdt.hour = 23; + endMdt.hour = 6; + startDt = StructToTime(startMdt) - 86400 + timeShiftSec; // prev day + endDt = StructToTime(endMdt) + timeShiftSec; + + if(currDt >= startDt && currDt < endDt) + { + addBox(&boxes, LIT_SESSION_TOKYO, startDt, endDt, low[i], high[i], i); + } + } + } + +//Print("Drawn boxes: ", boxes.Total()); + return rates_total; + } + +//+------------------------------------------------------------------+ +//| Get time zone offset in hours | +//+------------------------------------------------------------------+ +int getTimeZoneOffsetHours() + { + datetime serverTime = TimeTradeServer(); + datetime gmtTime = TimeGMT(); + + int offsetSeconds = ((int)serverTime) - ((int)gmtTime); + int offsetHours = offsetSeconds / 3600; + + Print("Detected server offset: ", IntegerToString(offsetHours), " hrs"); + return offsetHours; + } + +//+------------------------------------------------------------------+ +//| Add or update existing box and draw it | +//+------------------------------------------------------------------+ +void addBox(CArrayObj *allBoxes, ENUM_LIT_SESSION_TYPE type, datetime start, datetime end, double low, double high, int i) + { + Box *box = allBoxes.Total() > 0 + ? allBoxes.At(allBoxes.Total() - 1) + : NULL; + + double lowNormalized = NormalizeDouble(low, _Digits); + double highNormalized = NormalizeDouble(high, _Digits); + + if(box != NULL && box.start == start) + { + if(box.low > lowNormalized || box.high < highNormalized) + { + box.end = end; + box.low = MathMin(box.low, lowNormalized); + box.high = MathMax(box.high, highNormalized); + + ObjectsDeleteAll(0, "sbox " + TimeToString(start)); + + box.draw(); + Print("box redrawn"); + } + } + else + { + box = new Box(type, start, end, lowNormalized, highNormalized); + boxes.Add(box); + + box.draw(); + Print("new box drawn"); + } + + setBuffers(box, i); + } + +//+------------------------------------------------------------------+ +//| Fill indicator buffers | +//+------------------------------------------------------------------+ +void setBuffers(Box *box, int i) + { + TypeBuffer[i] = box.getTypeAsNumber(); + LowBuffer[i] = box.low; + HighBuffer[i] = box.high; + } +//+------------------------------------------------------------------+ diff --git a/Libraries/x-saherelm.common.lib.mq5 b/Libraries/x-saherelm.common.lib.mq5 index e9baf5db..b475e668 100644 --- a/Libraries/x-saherelm.common.lib.mq5 +++ b/Libraries/x-saherelm.common.lib.mq5 @@ -1105,6 +1105,59 @@ struct XOHCL return result; } + // + // Calculate Last Bar Open Time in Smaller Period ... + datetime GetLastBarTimeOfPeriod( + ENUM_TIMEFRAMES smallerPeriod // + ) + { + // + datetime result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + int sourceSeconds = PeriodSeconds(period); + int destSeconds = PeriodSeconds(smallerPeriod); + if (destSeconds >= sourceSeconds) + { + return result; + } + + // + result = (datetime)((((int)time) + sourceSeconds) - destSeconds); + + // + return result; + } + + // + // Calculate Close Time ... + datetime GetCloseTime() + { + // + datetime result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + int seconds = PeriodSeconds(period); + + // + result = (datetime)(((int)time) + seconds); + + // + return result; + } + // // Find Current Bar Index on Chart ... int Index() @@ -8145,6 +8198,51 @@ MqlDateTime GetBarTimeStruct( return result; } +// +// Retrieve Last Available Bar Time of +// Dest Period inside Source Period ... +datetime GetLastBarTime( + ENUM_TIMEFRAMES sourcePeriod, + ENUM_TIMEFRAMES destPeriod // +) +{ + // + datetime result = NULL; + + // + if (!IsValid(sourcePeriod) || + !IsValid(destPeriod)) + { + return result; + } + + // + int sourceSeconds = PeriodSeconds(sourcePeriod); + int destSeconds = PeriodSeconds(destPeriod); + if (sourceSeconds < destSeconds) + { + return result; + } + + // + XOHCL sourceBar; + bool isInited = sourceBar.Init( + _Symbol, + sourcePeriod, + 0 // + ); + if (!isInited) + { + return result; + } + + // + result = (datetime)((((int)sourceBar.time) + sourceSeconds) - destSeconds); + + // + return result; +} + // // Other Tools ...