
### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.base.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XBaseClass
// Description: provides all based classes for use ...
//
//
// 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"

//
// XBase Class ...
class XCBase
{
    //
    // Public ...
  public:
    //
    // Protected ...

    //
    // Represent Basic Unique Tag ...
    virtual string GetTag() {
      return NULL;
    }

    //
    // Retrieve Class Token ...
    virtual string GetToken() {
      return NULL;
    }

    //
    // Protected
  protected:
    //
    // Private ...
  private:
};

//
template <typename T>
string GenerateSpecifiedCommonSummary(
    T &mItem,
    string separator = "\n",
    bool includeScores = true,
    bool setLabel = false //
)
{
    //
    string result = NULL;

    //
    double bullishScore = 0;
    double bearishScore = 0;
    mItem.GenerateScore(
        bullishScore,
        bearishScore //
    );

    //
    result =
        //
        (!setLabel
             ? ""
             : "Commons:" + separator) +
        "---------------" + separator +
        "Symbol: " + mItem.symbol + separator +
        "Period: " + ToXString(mItem.period) + separator +
        "Time: " + ToXString(mItem.time) + separator +
        (includeScores
             ? "---------------" + separator +
                   "Scores:" + separator +
                   "---------------" + separator +
                   "Bullish: " + ToXString(bullishScore) + separator +
                   "Bearish: " + ToXString(bearishScore) + separator +
                   "---------------" + separator
             : "") +
        ""
        //
        ;

    //
    return result;
}

//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.session-detector.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class
// -------------------------------------------------
// Name: XCSessionDetector
// Description: Forex Session Detector with Event System
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////

//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm Forex Session Detector Class"
#property strict

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Libraries/x-saherelm.common.extensions.lib.mq5"
#include "../Libraries/x-saherelm.x-enums.lib.mq5"

//
// Definitions ...

// #region Session Types ...
//
// Forex Session Types ...
enum ENUM_X_SESSION_TYPE
{
    X_SESSION_NONE = 0,      // None
    X_SESSION_SYDNEY = 1,    // Sydney Session
    X_SESSION_TOKYO = 2,     // Tokyo Session
    X_SESSION_LONDON = 3,    // London Session
    X_SESSION_NEWYORK = 4,   // New York Session
    X_SESSION_FRANKFURT = 5, // Frankfurt Session
    X_SESSION_SINGAPORE = 6, // Singapore Session
    X_SESSION_HONGKONG = 7,  // Hong Kong Session
};

/**
 * Validate ...
 *
 * @param  value: ENUM_X_SESSION_TYPE member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_SESSION_TYPE value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_SESSION_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_SESSION_TYPE value)
{
    return IsValid(value);
}

/**
 * Cpnverts to String ...
 *
 * @param  value: ENUM_X_SESSION_TYPE ...
 *
 * @return ( string )
 */
string ToXString(ENUM_X_SESSION_TYPE value)
{
    //
    string result = NULL;

    //
    if (value == NULL)
    {
        return result;
    }

    //
    switch (value)
    {
    //
    case X_SESSION_NONE:
        result = "NONE";
        break;

    //
    case X_SESSION_SYDNEY:
        result = "SYDNEY";
        break;

    //
    case X_SESSION_TOKYO:
        result = "TOKYO";
        break;

    //
    case X_SESSION_LONDON:
        result = "LONDON";
        break;

    //
    case X_SESSION_NEWYORK:
        result = "NEWYORK";
        break;

    //
    case X_SESSION_FRANKFURT:
        result = "FRANKFURT";
        break;

    //
    case X_SESSION_SINGAPORE:
        result = "SINGAPORE";
        break;

    //
    case X_SESSION_HONGKONG:
        result = "HONGKONG";
        break;
    }

    //
    return result;
}

/**
 * Parse From String ...
 *
 * @param  value: string ...
 *
 * @return ( ENUM_X_SESSION_TYPE )
 */
ENUM_X_SESSION_TYPE FromXString(string value)
{
    //
    ENUM_X_SESSION_TYPE result = X_SESSION_NONE;

    //
    // Validate ...
    if (!IsXValid(value))
    {
        return result;
    }

    //
    if (value == ToXString(X_SESSION_NONE))
    {
        result = X_SESSION_NONE;
    }
    else if (value == ToXString(X_SESSION_SYDNEY))
    {
        result = X_SESSION_SYDNEY;
    }
    else if (value == ToXString(X_SESSION_TOKYO))
    {
        result = X_SESSION_TOKYO;
    }
    else if (value == ToXString(X_SESSION_LONDON))
    {
        result = X_SESSION_LONDON;
    }
    else if (value == ToXString(X_SESSION_NEWYORK))
    {
        result = X_SESSION_NEWYORK;
    }
    else if (value == ToXString(X_SESSION_FRANKFURT))
    {
        result = X_SESSION_FRANKFURT;
    }
    else if (value == ToXString(X_SESSION_SINGAPORE))
    {
        result = X_SESSION_SINGAPORE;
    }
    else if (value == ToXString(X_SESSION_HONGKONG))
    {
        result = X_SESSION_HONGKONG;
    }

    //
    return result;
}
// #endregion

// #region Session Overlap Types ...
//
// Session Overlap Types ...
enum ENUM_X_SESSION_OVERLAP_TYPE
{
    X_OVERLAP_NONE = 0,             // None
    X_OVERLAP_SYDNEY_TOKYO = 1,     // Sydney-Tokyo Overlap
    X_OVERLAP_TOKYO_LONDON = 2,     // Tokyo-London Overlap
    X_OVERLAP_LONDON_NEWYORK = 3,   // London-New York Overlap (Most Liquid)
    X_OVERLAP_LONDON_FRANKFURT = 4, // London-Frankfurt Overlap
    X_OVERLAP_NEWYORK_SYDNEY = 5,   // New York-Sydney Overlap
    X_OVERLAP_TOKYO_SINGAPORE = 6,  // Tokyo-Singapore Overlap
    X_OVERLAP_HONGKONG_TOKYO = 7,   // Hong Kong-Tokyo Overlap
};

/**
 * Validate ...
 *
 * @param  value: ENUM_X_SESSION_OVERLAP_TYPE member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_SESSION_OVERLAP_TYPE value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_OVERLAP_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_SESSION_OVERLAP_TYPE value)
{
    return IsValid(value);
}

/**
 * Cpnverts to String ...
 *
 * @param  value: ENUM_X_SESSION_OVERLAP_TYPE ...
 *
 * @return ( string )
 */
string ToXString(ENUM_X_SESSION_OVERLAP_TYPE value)
{
    //
    string result = NULL;

    //
    // Validate ...
    if (value == NULL)
    {
        return result;
    }

    //
    switch (value)
    {
    //
    case X_OVERLAP_NONE:
        result = "NONE";
        break;

    //
    case X_OVERLAP_SYDNEY_TOKYO:
        result = "SYDNEY_TOKYO";
        break;

    //
    case X_OVERLAP_TOKYO_LONDON:
        result = "TOKYO_LONDON";
        break;

    //
    case X_OVERLAP_LONDON_NEWYORK:
        result = "LONDON_NEWYORK";
        break;

    //
    case X_OVERLAP_LONDON_FRANKFURT:
        result = "LONDON_FRANKFURT";
        break;

    //
    case X_OVERLAP_NEWYORK_SYDNEY:
        result = "NEWYORK_SYDNEY";
        break;

    //
    case X_OVERLAP_TOKYO_SINGAPORE:
        result = "TOKYO_SINGAPORE";
        break;

    //
    case X_OVERLAP_HONGKONG_TOKYO:
        result = "HONGKONG_TOKYO";
        break;
    }

    //
    return result;
}
// #endregion

// #region Session State ...
//
// Session State ...
enum ENUM_X_SESSION_STATE
{
    X_SESSION_STATE_INACTIVE = 0, // Session Not Active
    X_SESSION_STATE_ACTIVE = 1,   // Session Active
    X_SESSION_STATE_STARTING = 2, // Session Starting (First Bar)
    X_SESSION_STATE_ENDING = 3,   // Session Ending (Last Bar)
};

/**
 * Check a Session State is Active or not ...
 *
 * @param  value: ENUM_X_SESSION_STATE
 *
 * @return ( bool )
 */
bool IsXActive(ENUM_X_SESSION_STATE value)
{
    //
    bool result = false;

    //
    result =
        value == X_SESSION_STATE_ACTIVE;

    //
    return result;
}
// #endregion

// #region Event Handler Types ...
typedef void (*TOnSessionStart)(ENUM_X_SESSION_TYPE session, datetime startTime);
typedef void (*TOnSessionEnd)(ENUM_X_SESSION_TYPE session, datetime endTime);
typedef void (*TOnOverlapStart)(ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime startTime);
typedef void (*TOnOverlapEnd)(ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime endTime);
typedef void (*TOnSessionStateChange)(ENUM_X_SESSION_TYPE session, ENUM_X_SESSION_STATE state);
// #endregion

// #region Session Configuration ...
struct XSessionConfig
{
    //
    string name;                // Session Name
    ENUM_X_SESSION_TYPE type;   // Session Type
    string startTime;           // Start Time (HH:MM)
    string endTime;             // End Time (HH:MM)
    color sessionColor;         // Session Color for Visualization
    bool isActive;              // Is Session Active
    datetime lastStartTime;     // Last Start Time
    datetime lastEndTime;       // Last End Time
    ENUM_X_SESSION_STATE state; // Current State

    //
    // Constructor ...
    XSessionConfig()
    {
        Clean();
    }

    //
    // Tools ...

    //
    void Clean()
    {
        //
        name = NULL;
        type = X_SESSION_NONE;
        startTime = NULL;
        endTime = NULL;
        sessionColor = CLR_NONE;
        isActive = false;
        lastStartTime = NULL;
        lastEndTime = NULL;
        state = X_SESSION_STATE_INACTIVE;

        //
        ZeroMemory(this);
    }

    //
    void Default(ENUM_X_SESSION_TYPE _type)
    {
        //
        Clean();

        //
        // Validate ...
        if (!IsXValid(_type))
        {
            return;
        }

        //
        type = _type;
        name = ToXString(_type);

        //
        switch (_type)
        {
        //
        case X_SESSION_SYDNEY:
            startTime = "22:00";
            endTime = "07:00";
            sessionColor = clrAqua;
            break;

        //
        case X_SESSION_TOKYO:
            startTime = "00:00";
            endTime = "09:00";
            sessionColor = clrYellow;
            break;

        //
        case X_SESSION_SINGAPORE:
            startTime = "01:00";
            endTime = "09:00";
            sessionColor = clrOrange;
            break;

        //
        case X_SESSION_HONGKONG:
            startTime = "01:00";
            endTime = "09:00";
            sessionColor = clrGold;
            break;

        //
        case X_SESSION_FRANKFURT:
            startTime = "07:00";
            endTime = "16:00";
            sessionColor = clrLime;
            break;

        //
        case X_SESSION_LONDON:
            startTime = "08:00";
            endTime = "17:00";
            sessionColor = clrGreen;
            break;

        //
        case X_SESSION_NEWYORK:
            startTime = "13:00";
            endTime = "22:00";
            sessionColor = clrRed;
            break;

        //
        default:
            name = "Unknown";
            startTime = "00:00";
            endTime = "00:00";
            sessionColor = clrGray;
            break;
        }
    }

    //
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            IsXValid(type) &&
            IsXValid(name) &&
            IsXValid(startTime) &&
            IsXValid(endTime);

        //
        return result;
    }
};
// #endregion

// #region Overlap Configuration ...
struct XOverlapConfig
{
    //
    string name;                      // Overlap Name
    ENUM_X_SESSION_OVERLAP_TYPE type; // Overlap Type
    ENUM_X_SESSION_TYPE session1;     // First Session
    ENUM_X_SESSION_TYPE session2;     // Second Session
    string overlapStartTime;          // Overlap Start Time
    string overlapEndTime;            // Overlap End Time
    color overlapColor;               // Overlap Color
    bool isActive;                    // Is Overlap Active
    datetime lastStartTime;           // Last Start Time
    datetime lastEndTime;             // Last End Time

    //
    // Constructor ...
    XOverlapConfig()
    {
        Clean();
    }

    //
    // Tools ...

    //
    void Clean()
    {
        //
        name = NULL;
        type = X_OVERLAP_NONE;
        session1 = X_SESSION_NONE;
        session2 = X_SESSION_NONE;
        overlapStartTime = NULL;
        overlapEndTime = NULL;
        overlapColor = CLR_NONE;
        isActive = false;
        lastStartTime = NULL;
        lastEndTime = NULL;

        //
        ZeroMemory(this);
    }

    //
    void Default(ENUM_X_SESSION_OVERLAP_TYPE _type)
    {
        //
        Clean();

        //
        if (!IsXValid(_type))
        {
            return;
        }

        //
        type = _type;
        name = ToXString(_type);

        //
        switch (_type)
        {
        //
        case X_OVERLAP_SYDNEY_TOKYO:
            session1 = X_SESSION_SYDNEY;
            session2 = X_SESSION_TOKYO;
            overlapStartTime = "00:00";
            overlapEndTime = "07:00";
            overlapColor = clrYellow;
            break;

        //
        case X_OVERLAP_TOKYO_LONDON:
            session1 = X_SESSION_TOKYO;
            session2 = X_SESSION_LONDON;
            overlapStartTime = "08:00";
            overlapEndTime = "09:00";
            overlapColor = clrLime;
            break;

        //
        case X_OVERLAP_LONDON_NEWYORK:
            session1 = X_SESSION_LONDON;
            session2 = X_SESSION_NEWYORK;
            overlapStartTime = "13:00";
            overlapEndTime = "17:00";
            overlapColor = clrOrange;
            break;

        //
        case X_OVERLAP_LONDON_FRANKFURT:
            session1 = X_SESSION_LONDON;
            session2 = X_SESSION_FRANKFURT;
            overlapStartTime = "08:00";
            overlapEndTime = "16:00";
            overlapColor = clrGreen;
            break;

        //
        case X_OVERLAP_NEWYORK_SYDNEY:
            session1 = X_SESSION_NEWYORK;
            session2 = X_SESSION_SYDNEY;
            overlapStartTime = "22:00";
            overlapEndTime = "22:00";
            overlapColor = clrPurple;
            break;

        //
        case X_OVERLAP_TOKYO_SINGAPORE:
            session1 = X_SESSION_TOKYO;
            session2 = X_SESSION_SINGAPORE;
            overlapStartTime = "01:00";
            overlapEndTime = "09:00";
            overlapColor = clrGold;
            break;

        //
        case X_OVERLAP_HONGKONG_TOKYO:
            session1 = X_SESSION_HONGKONG;
            session2 = X_SESSION_TOKYO;
            overlapStartTime = "01:00";
            overlapEndTime = "09:00";
            overlapColor = clrPink;
            break;

        //
        default:
            name = "Unknown";
            session1 = X_SESSION_NONE;
            session2 = X_SESSION_NONE;
            overlapStartTime = "00:00";
            overlapEndTime = "00:00";
            overlapColor = clrGray;
            break;
        }
    }

    //
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            IsXValid(type) &&
            IsXValid(name) &&
            IsXValid(session1) &&
            IsXValid(session2);

        //
        return result;
    }
};
// #endregion

//
// Implementation ...
class XCSessionDetector : public XCBase
{
    //
  public:
    //
    // Props ...
    //
    XCAlert *alert;            // Alert Handler
    XSessionConfig sessions[]; // All Sessions
    XOverlapConfig overlaps[]; // All Overlaps

    //
    // Event Handlers ...
    TOnSessionEnd mOnSessionEndHandlers[];
    TOnOverlapEnd mOnOverlapEndHandlers[];
    TOnSessionStart mOnSessionStartHandlers[];
    TOnOverlapStart mOnOverlapStartHandlers[];
    TOnSessionStateChange mOnSessionStateChangeHandlers[];

    //
    // Time Tracking ...
    datetime lastCheckTime; // Last Check Time
    int timezoneOffset;     // Timezone Offset (in Hours)
    bool useServerTime;     // Use Server Time or UTC

    //
    // Constructor ...
    XCSessionDetector(
        XCAlert *_alert = NULL,
        bool _useServerTime = true,
        int _timezoneOffset = 0 //
    )
    {
        alert = _alert;
        useServerTime = _useServerTime;
        timezoneOffset = _timezoneOffset;
        lastCheckTime = NULL;

        //
        InitializeSessions();
        InitializeOverlaps();
    }

    //
    // Deconstructor ...
    ~XCSessionDetector()
    {
        //
        Clean(sessions);
        Clean(overlaps);
        Clean(mOnSessionEndHandlers);
        Clean(mOnOverlapEndHandlers);
        Clean(mOnSessionStartHandlers);
        Clean(mOnOverlapStartHandlers);
        Clean(mOnSessionStateChangeHandlers);

        //
        ZeroMemory(alert);
    }

    // #region Getters ...
    /**
     * Get Session Config by Type
     *
     * @param  sessionType: ENUM_X_SESSION_TYPE
     *
     * @return ( XSessionConfig )
     */
    XSessionConfig GetSessionConfig(ENUM_X_SESSION_TYPE sessionType)
    {
        //
        XSessionConfig result;
        result.Clean();

        //
        if (!IsXValid(sessionType))
        {
            return result;
        }

        //
        int sessionsCount = ArraySize(sessions);
        for (int i = 0; i < sessionsCount; i++)
        {
            //
            if (sessions[i].type == sessionType)
            {
                result = sessions[i];
                break;
            }
        }

        //
        return result;
    }

    /**
     * Get Overlap Config by Type
     *
     * @param  overlapType: ENUM_X_SESSION_OVERLAP_TYPE
     *
     * @return ( XOverlapConfig )
     */
    XOverlapConfig GetOverlapConfig(ENUM_X_SESSION_OVERLAP_TYPE overlapType)
    {
        //
        XOverlapConfig result;
        result.Clean();

        //
        if (!IsXValid(overlapType))
        {
            return result;
        }

        //
        int overlapsCount = ArraySize(overlaps);
        for (int i = 0; i < overlapsCount; i++)
        {
            //
            if (overlaps[i].type == overlapType)
            {
                result = overlaps[i];
                break;
            }
        }

        //
        return result;
    }

    /**
     * Get Current Active Session
     *
     * @param  currentTime: datetime
     *
     * @return ( ENUM_X_SESSION_TYPE )
     */
    ENUM_X_SESSION_TYPE GetCurrentSession(datetime currentTime = NULL)
    {
        //
        ENUM_X_SESSION_TYPE result = X_SESSION_NONE;

        //
        if (!IsXValid(currentTime))
        {
            currentTime = GetCurrentTime();
        }

        //
        int sessionsCount = ArraySize(sessions);
        for (int i = 0; i < sessionsCount; i++)
        {
            //
            if (IsSessionActive(sessions[i].type, currentTime))
            {
                result = sessions[i].type;
                break;
            }
        }

        //
        return result;
    }

    /**
     * Get Current Active Overlap
     *
     * @param  currentTime: datetime
     *
     * @return ( ENUM_X_SESSION_OVERLAP_TYPE )
     */
    ENUM_X_SESSION_OVERLAP_TYPE GetCurrentOverlap(datetime currentTime = NULL)
    {
        //
        ENUM_X_SESSION_OVERLAP_TYPE result = X_OVERLAP_NONE;

        //
        if (!IsXValid(currentTime))
        {
            currentTime = GetCurrentTime();
        }

        //
        int overlapsCount = ArraySize(overlaps);
        for (int i = 0; i < overlapsCount; i++)
        {
            //
            if (IsOverlapActive(overlaps[i], currentTime))
            {
                result = overlaps[i].type;
                break;
            }
        }

        //
        return result;
    }
    // #endregion

    // #region Tools & Actions ...
    /**
     * Initialize All Forex Sessions
     */
    void InitializeSessions()
    {
        //
        Clean(sessions);

        //
        // Sydney Session
        XSessionConfig sydney;
        sydney.Default(X_SESSION_SYDNEY);
        AddRef(sydney, sessions);

        //
        // Tokyo Session
        XSessionConfig tokyo;
        tokyo.Default(X_SESSION_TOKYO);
        AddRef(tokyo, sessions);

        //
        // Singapore Session
        XSessionConfig singapore;
        singapore.Default(X_SESSION_SINGAPORE);
        AddRef(singapore, sessions);

        //
        // Hong Kong Session
        XSessionConfig hongkong;
        hongkong.Default(X_SESSION_HONGKONG);
        AddRef(hongkong, sessions);

        //
        // Frankfurt Session
        XSessionConfig frankfurt;
        frankfurt.Default(X_SESSION_FRANKFURT);
        AddRef(frankfurt, sessions);

        //
        // London Session
        XSessionConfig london;
        london.Default(X_SESSION_LONDON);
        AddRef(london, sessions);

        //
        // New York Session
        XSessionConfig newyork;
        newyork.Default(X_SESSION_NEWYORK);
        AddRef(newyork, sessions);
    }

    /**
     * Initialize All Session Overlaps
     */
    void InitializeOverlaps()
    {
        //
        Clean(overlaps);

        //
        // Sydney-Tokyo Overlap
        XOverlapConfig sydneyTokyo;
        sydneyTokyo.Default(X_OVERLAP_SYDNEY_TOKYO);
        AddRef(sydneyTokyo, overlaps);

        //
        // Tokyo-London Overlap
        XOverlapConfig tokyoLondon;
        tokyoLondon.Default(X_OVERLAP_TOKYO_LONDON);
        AddRef(tokyoLondon, overlaps);

        //
        // London-New York Overlap (Most Important)
        XOverlapConfig londonNewYork;
        londonNewYork.Default(X_OVERLAP_LONDON_NEWYORK);
        AddRef(londonNewYork, overlaps);

        //
        // London-Frankfurt Overlap
        XOverlapConfig londonFrankfurt;
        londonFrankfurt.Default(X_OVERLAP_LONDON_FRANKFURT);
        AddRef(londonFrankfurt, overlaps);

        //
        // New York-Sydney Overlap
        XOverlapConfig newyorkSydney;
        newyorkSydney.Default(X_OVERLAP_NEWYORK_SYDNEY);
        AddRef(newyorkSydney, overlaps);
    }

    /**
     * Get Current Time (Adjusted for Timezone)
     *
     * @return ( datetime )
     */
    datetime GetCurrentTime()
    {
        //
        datetime currentTime = useServerTime
                                   ? TimeCurrent()
                                   : TimeGMT();

        //
        if (timezoneOffset != 0)
        {
            currentTime = (datetime)((int)currentTime + (timezoneOffset * 3600));
        }

        //
        return currentTime;
    }

    /**
     * Parse Time String to DateTime (Today's Date + Time)
     *
     * @param  timeString: String (HH:MM format)
     * @param  baseDate: datetime (Base Date)
     *
     * @return ( datetime )
     */
    datetime ParseTimeToDateTime(
        string timeString,
        datetime baseDate //
    )
    {
        //
        datetime result = NULL;

        //
        if (!IsXValid(timeString))
        {
            return result;
        }

        //
        MqlDateTime dt;
        TimeToStruct(baseDate, dt);

        //
        string parts[];
        int partsCount = SplitContent(parts, timeString, ":");
        if (partsCount >= 2)
        {
            //
            dt.hour = (int)parts[0];
            dt.min = (int)parts[1];
            dt.sec = 0;

            //
            result = StructToTime(dt);
        }

        //
        return result;
    }

    /**
     * Check if Current Time is Within Session Time Range
     *
     * @param  startTime: String (HH:MM)
     * @param  endTime: String (HH:MM)
     * @param  currentTime: datetime
     *
     * @return ( bool )
     */
    bool IsTimeInRange(
        string startTime,
        string endTime,
        datetime currentTime //
    )
    {
        //
        bool result = false;

        //
        if (!IsXValid(startTime) || !IsXValid(endTime))
        {
            return result;
        }

        //
        datetime todayStart = ParseTimeToDateTime(startTime, currentTime);
        datetime todayEnd = ParseTimeToDateTime(endTime, currentTime);

        //
        // Handle Sessions that Cross Midnight
        if (todayEnd < todayStart)
        {
            //
            // Session crosses midnight
            result = (currentTime >= todayStart || currentTime < todayEnd);
        }
        else
        {
            //
            // Normal session
            result = (currentTime >= todayStart && currentTime < todayEnd);
        }

        //
        return result;
    }

    /**
     * Check if Current Time is Within Overlap Time Range
     *
     * @param  overlap: XOverlapConfig reference
     * @param  currentTime: datetime
     *
     * @return ( bool )
     */
    bool IsOverlapActive(
        XOverlapConfig &overlap,
        datetime currentTime //
    )
    {
        //
        bool result = false;

        //
        if (!overlap.IsValid())
        {
            return result;
        }

        //
        // Check if Both Sessions are Active
        bool session1Active = IsSessionActive(overlap.session1, currentTime);
        bool session2Active = IsSessionActive(overlap.session2, currentTime);

        //
        result =
            session1Active &&
            session2Active;

        //
        return result;
    }

    /**
     * Check if Specific Session is Active
     *
     * @param  sessionType: ENUM_X_SESSION_TYPE
     * @param  currentTime: datetime
     *
     * @return ( bool )
     */
    bool IsSessionActive(
        ENUM_X_SESSION_TYPE sessionType,
        datetime currentTime //
    )
    {
        //
        bool result = false;

        //
        int sessionsCount = ArraySize(sessions);
        for (int i = 0; i < sessionsCount; i++)
        {
            //
            if (sessions[i].type == sessionType)
            {
                //
                result = IsTimeInRange(
                    sessions[i].startTime,
                    sessions[i].endTime,
                    currentTime //
                );

                //
                break;
            }
        }

        //
        return result;
    }

    /**
     * Get Active Sessions
     *
     * @param  activeSessions: ENUM_X_SESSION_TYPE collection reference
     * @param  currentTime: datetime
     *
     * @return ( int )
     */
    int GetActiveSessions(
        ENUM_X_SESSION_TYPE &activeSessions[],
        datetime currentTime = NULL //
    )
    {
        //
        int result = 0;
        Clean(activeSessions);

        //
        if (!IsXValid(currentTime))
        {
            currentTime = GetCurrentTime();
        }

        //
        int sessionsCount = ArraySize(sessions);
        for (int i = 0; i < sessionsCount; i++)
        {
            //
            if (IsSessionActive(sessions[i].type, currentTime))
            {
                Add(sessions[i].type, activeSessions);
            }
        }

        //
        result = ArraySize(activeSessions);

        //
        return result;
    }

    /**
     * Get Active Overlaps
     *
     * @param  activeOverlaps: ENUM_X_SESSION_OVERLAP_TYPE collection reference
     * @param  currentTime: datetime
     *
     * @return ( int )
     */
    int GetActiveOverlaps(
        ENUM_X_SESSION_OVERLAP_TYPE &activeOverlaps[],
        datetime currentTime = NULL //
    )
    {
        //
        int result = 0;
        Clean(activeOverlaps);

        //
        if (!IsXValid(currentTime))
        {
            currentTime = GetCurrentTime();
        }

        //
        int overlapsCount = ArraySize(overlaps);
        for (int i = 0; i < overlapsCount; i++)
        {
            //
            if (IsOverlapActive(overlaps[i], currentTime))
            {
                Add(overlaps[i].type, activeOverlaps);
            }
        }

        //
        result = ArraySize(activeOverlaps);

        //
        return result;
    }
    // #endregion

    // #region Processors ...
    /**
     * Process Session Detection (Call on Every Tick or Bar)
     *
     * @param  currentTime: datetime (Optional, uses current time if NULL)
     */
    void Process(datetime currentTime = NULL)
    {
        //
        if (!IsXValid(currentTime))
        {
            currentTime = GetCurrentTime();
        }

        //
        // Check if Enough Time Passed Since Last Check (Prevent Multiple Fires)
        if (IsXValid(lastCheckTime) &&
            (int)currentTime - (int)lastCheckTime < 60) // Minimum 1 minute between checks
        {
            return;
        }

        //
        lastCheckTime = currentTime;

        //
        // Process All Sessions
        int sessionsCount = ArraySize(sessions);
        for (int i = 0; i < sessionsCount; i++)
        {
            ProcessSession(sessions[i], currentTime);
        }

        //
        // Process All Overlaps
        int overlapsCount = ArraySize(overlaps);
        for (int i = 0; i < overlapsCount; i++)
        {
            ProcessOverlap(overlaps[i], currentTime);
        }
    }

    /**
     * Process Individual Session
     *
     * @param  session: XSessionConfig reference
     * @param  currentTime: datetime
     */
    void ProcessSession(
        XSessionConfig &session,
        datetime currentTime //
    )
    {
        //
        if (!session.IsValid())
        {
            return;
        }

        //
        bool wasActive = session.isActive;
        bool isActive = IsTimeInRange(
            session.startTime,
            session.endTime,
            currentTime //
        );

        //
        // Session Started
        if (isActive && !wasActive)
        {
            //
            session.isActive = true;
            session.lastStartTime = currentTime;
            session.state = X_SESSION_STATE_STARTING;

            //
            NotifySessionStart(session.type, currentTime);
            NotifySessionStateChange(session.type, session.state);

            //
            // Log Alert
            if (alert != NULL)
            {
                //
                string message = "Session Started: " + session.name +
                                 " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS);

                //
                alert.SendAlert(message);
            }
        }
        //
        // Session Ended
        else if (!isActive && wasActive)
        {
            //
            session.isActive = false;
            session.lastEndTime = currentTime;
            session.state = X_SESSION_STATE_ENDING;

            //
            NotifySessionEnd(session.type, currentTime);
            NotifySessionStateChange(session.type, session.state);

            //
            // Log Alert
            if (alert != NULL)
            {
                //
                string message = "Session Ended: " + session.name +
                                 " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS);

                //
                alert.SendAlert(message);
            }
        }
        //
        // Session Active (Ongoing)
        else if (isActive && wasActive)
        {
            session.state = X_SESSION_STATE_ACTIVE;
        }
        //
        // Session Inactive
        else
        {
            session.state = X_SESSION_STATE_INACTIVE;
        }
    }

    /**
     * Process Individual Overlap
     *
     * @param  overlap: XOverlapConfig reference
     * @param  currentTime: datetime
     */
    void ProcessOverlap(
        XOverlapConfig &overlap,
        datetime currentTime //
    )
    {
        //
        if (!overlap.IsValid())
        {
            return;
        }

        //
        bool wasActive = overlap.isActive;
        bool isActive = IsOverlapActive(overlap, currentTime);

        //
        // Overlap Started
        if (isActive && !wasActive)
        {
            //
            overlap.isActive = true;
            overlap.lastStartTime = currentTime;

            //
            NotifyOverlapStart(overlap.type, currentTime);

            //
            // Log Alert
            if (alert != NULL)
            {
                //
                string message = "Overlap Started: " + overlap.name +
                                 " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS);

                //
                alert.SendAlert(message);
            }
        }
        //
        // Overlap Ended
        else if (!isActive && wasActive)
        {
            //
            overlap.isActive = false;
            overlap.lastEndTime = currentTime;

            //
            NotifyOverlapEnd(overlap.type, currentTime);

            //
            // Log Alert
            if (alert != NULL)
            {
                //
                string message = "Overlap Ended: " + overlap.name +
                                 " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS);

                //
                alert.SendAlert(message);
            }
        }
    }
    // #endregion

    // #region Event Listener Registration ...
    /**
     * Add Session Start Event Handler
     *
     * @param  handler: TOnSessionStart
     */
    void AddOnSessionStartHandler(TOnSessionStart handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnSessionStartHandlers);
    }

    /**
     * Add Session End Event Handler
     *
     * @param  handler: TOnSessionEnd
     */
    void AddOnSessionEndHandler(TOnSessionEnd handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnSessionEndHandlers);
    }

    /**
     * Add Overlap Start Event Handler
     *
     * @param  handler: TOnOverlapStart
     */
    void AddOnOverlapStartHandler(TOnOverlapStart handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnOverlapStartHandlers);
    }

    /**
     * Add Overlap End Event Handler
     *
     * @param  handler: TOnOverlapEnd
     */
    void AddOnOverlapEndHandler(TOnOverlapEnd handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnOverlapEndHandlers);
    }

    /**
     * Add Session State Change Event Handler
     *
     * @param  handler: TOnSessionStateChange
     */
    void AddOnSessionStateChangeHandler(TOnSessionStateChange handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnSessionStateChangeHandlers);
    }
    // #endregion

    //
  protected:
    //

    // #region Event Notifiers ...
    /**
     * Notify Session Start Event
     *
     * @param  session: ENUM_X_SESSION_TYPE
     * @param  startTime: datetime
     */
    void NotifySessionStart(
        ENUM_X_SESSION_TYPE session,
        datetime startTime //
    )
    {
        //
        // Validate ...
        if (!IsXValid(session))
        {
            return;
        }

        //
        // Multiple Handlers ...
        int count = ArraySize(mOnSessionStartHandlers);
        for (int i = 0; i < count; i++)
        {
            //
            mOnSessionStartHandlers[i](
                session,
                startTime //
            );
        }
    }

    /**
     * Notify Session End Event
     *
     * @param  session: ENUM_X_SESSION_TYPE
     * @param  endTime: datetime
     */
    void NotifySessionEnd(
        ENUM_X_SESSION_TYPE session,
        datetime endTime //
    )
    {
        //
        // Validate ...
        if (!IsXValid(session))
        {
            return;
        }

        //
        // Multiple Handlers ...
        int count = ArraySize(mOnSessionEndHandlers);
        for (int i = 0; i < count; i++)
        {
            //
            mOnSessionEndHandlers[i](
                session,
                endTime //
            );
        }
    }

    /**
     * Notify Overlap Start Event
     *
     * @param  overlap: ENUM_X_SESSION_OVERLAP_TYPE
     * @param  startTime: datetime
     */
    void NotifyOverlapStart(
        ENUM_X_SESSION_OVERLAP_TYPE overlap,
        datetime startTime //
    )
    {
        //
        // Validate ...
        if (!IsXValid(overlap))
        {
            return;
        }

        //
        // Multiple Handlers ...
        int count = ArraySize(mOnOverlapStartHandlers);
        for (int i = 0; i < count; i++)
        {
            //
            mOnOverlapStartHandlers[i](
                overlap,
                startTime //
            );
        }
    }

    /**
     * Notify Overlap End Event
     *
     * @param  overlap: ENUM_X_SESSION_OVERLAP_TYPE
     * @param  endTime: datetime
     */
    void NotifyOverlapEnd(
        ENUM_X_SESSION_OVERLAP_TYPE overlap,
        datetime endTime //
    )
    {
        //
        // Validate ...
        if (!IsXValid(overlap))
        {
            return;
        }

        //
        // Multiple Handlers ...
        int count = ArraySize(mOnOverlapEndHandlers);
        for (int i = 0; i < count; i++)
        {
            //
            mOnOverlapEndHandlers[i](
                overlap,
                endTime //
            );
        }
    }

    /**
     * Notify Session State Change Event
     *
     * @param  session: ENUM_X_SESSION_TYPE
     * @param  state: ENUM_X_SESSION_STATE
     */
    void NotifySessionStateChange(
        ENUM_X_SESSION_TYPE session,
        ENUM_X_SESSION_STATE state //
    )
    {
        //
        // Validate ...
        if (!IsXValid(session))
        {
            return;
        }

        //
        // Multiple Handlers ...
        int count = ArraySize(mOnSessionStateChangeHandlers);
        for (int i = 0; i < count; i++)
        {
            //
            mOnSessionStateChangeHandlers[i](
                session,
                state);
        }
    }
    // #endregion

  private:
    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-account.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCAccount
// Description: provides all Account requirements ...
//
//
// 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

//
// START Definitions ...
//

//
// END Definitions ...
//

//
// START Import and Inclused requirements ...
//

//
// Includes ...
#include "../Classes/x-saherelm.base.class.mq5"
#include <Trade/AccountInfo.mqh>

//
// END Import and Inclused requirements ...
//

//
// START Overrides ...
//

//
// END Overrides ...
//

//
// a Class for Manage Account ...
class XCAccount : public XCBase
{
    //
    // Public ...
  public:
    //
    // Constructor ...
    XCAccount()
    {
        //
        mAccountInfo = new CAccountInfo();
    }

    //
    // Deconstructor ...
    ~XCAccount()
    {
    }

    //
    // START Provided Functions ...
    //

    //
    // User Account ...
    long GetUserAccount()
    {
        return mAccountInfo.Login();
    }

    //
    // Account Leverage ...
    long GetLeverage()
    {
        return mAccountInfo.Leverage();
    }

    //
    // Get Trade Expert State ...
    bool CanExpertTrade()
    {
        return mAccountInfo.TradeExpert();
    }

    //
    // User Account Trade Mopde (ENUM_ACCOUNT_TRADE_MODE) ...
    // --------------------------
    // ACCOUNT_TRADE_MODE_DEMO
    // ACCOUNT_TRADE_MODE_CONTEST
    // ACCOUNT_TRADE_MODE_REAL
    ENUM_ACCOUNT_TRADE_MODE GetTradeMode()
    {
        return mAccountInfo.TradeMode();
    }

    //
    // Get Account Balance ...
    double GetBalance()
    {
        return mAccountInfo.Balance();
    }

    //
    // Get the amount of give Credit ...
    double GetCredit()
    {
        return mAccountInfo.Credit();
    }

    //
    // Get the amount of current Profit on account ...
    double GetProfit()
    {
        return mAccountInfo.Profit();
    }

    //
    // Get the amount of current Equity on account ...
    double GetEquity()
    {
        return mAccountInfo.Equity();
    }

    //
    // Get the amount of reserved Margin ...
    double GetMargin()
    {
        return mAccountInfo.Margin();
    }

    //
    // Get the amount of free Margin ...
    double GetFreeMargin()
    {
        return mAccountInfo.FreeMargin();
    }

    //
    // Get the Level of Margin ...
    double GetMarginLevel()
    {
        return mAccountInfo.MarginLevel();
    }

    //
    // Get the Level Of Margin for a Deposit ...
    double GetMarginCall()
    {
        return mAccountInfo.MarginCall();
    }

    //
    // Get the Level of Margin for Stop out ...
    double GetMarginStopOut()
    {
        return mAccountInfo.MarginStopOut();
    }

    //
    // Get the Client Name ...
    string GetName()
    {
        return mAccountInfo.Name();
    }

    //
    // Get the Trade Server Name ...
    string GetServerName()
    {
        return mAccountInfo.Server();
    }

    //
    // Get deposit Currency Name ...
    string GetCurrency()
    {
        return mAccountInfo.Currency();
    }

    //
    // Get the Company Name that serves an Account ...
    string GetCompany()
    {
        return mAccountInfo.Company();
    }

    //
    // Calculate Profits for the current account based on passed parameters ...
    double CalculateTradeProfit(
        const string symbol,  // trading symbol
        ENUM_ORDER_TYPE type, // order type
        double volume,        // volume
        double entry,         // open price
        double exit           // close price
    )
    {
        //
        double result = mAccountInfo.OrderProfitCheck(
            symbol,
            type,
            volume,
            entry,
            exit);

        //
        return result;
    }

    //
    // Calculate amount of margin which required for trade operation ...
    double CalculateMarging(
        const string symbol,  // trading symbol
        ENUM_ORDER_TYPE type, // order type
        double volume,        // volume
        double entry          // open price
    )
    {
        //
        double result = mAccountInfo.MarginCheck(
            symbol,
            type,
            volume,
            entry);

        //
        return result;
    }

    //
    // Calculate amount of free margin left after trade operation ...
    double CalculateFreeMarging(
        const string symbol,  // trading symbol
        ENUM_ORDER_TYPE type, // order type
        double volume,        // volume
        double entry          // open price
    )
    {
        //
        double result = mAccountInfo.FreeMarginCheck(
            symbol,
            type,
            volume,
            entry);

        //
        return result;
    }

    //
    // Calculate the Maximum possible volume of trade operation ...
    double CalculateMaxVolume(
        const string symbol,  // trading symbol
        ENUM_ORDER_TYPE type, // order type
        double entry,         // open price
        double percent = 100  // percent of available margin
    )
    {
        //
        double result = mAccountInfo.MaxLotCheck(
            symbol,
            type,
            entry,
            percent);

        //
        return result;
    }

    //
    // Calculate Point Value for Given Symbol based on Account ...
    double GetPointValue(
        string symbol // trading symbol
    )
    {
        //
        double result = 0;

        //
        double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
        double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
        double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

        //
        double ticksPerPoint = tickSize / point;

        //
        result = tickValue / ticksPerPoint;

        //
        return result;
    }

    //
    // Calculate Risk Amount based on account Currency by Providing:
    // Points and Volume ...
    double CalculateRiskAmount(
        string symbol, // trading symbol
        double points, // amount of Risk Points
        double volume  // position Volume
    )
    {
        //
        double result = 0;

        //
        double pointValue = GetPointValue(symbol);

        //
        result = pointValue * volume * points;

        //
        return result;
    }

    //
    // Calculate Risk Points based on account Currency by Providing:
    // Volume and Amount ...
    double CalculateRiskPoints(
        string symbol, // trading symbol
        double volume, // position Volume
        double amount  // amount of Risk based on Account Currency
    )
    {
        //
        double result = 0;

        //
        double pointValue = GetPointValue(symbol);

        //
        result = amount / (pointValue * volume);

        //
        return result;
    }

    //
    // Calculate Volume based on account Currency by Providing:
    // Amount and Risk Points ...
    double CalculateVolume(
        string symbol, // trading symbol
        double amount, // amount of Risk based on Account Currency
        double points  // amount of Risk Points
    )
    {
        //
        double result = 0;

        //
        points =
            points <= 0
                ? 10
                : points;

        //
        double pointValue = GetPointValue(symbol);

        //
        result = amount / (pointValue * points);

        //
        return result;
    }

    /**
     * Retrieve Specified Message for Report Balance ...
     *
     * @return ( string )
     */
    string GetBalanceReportMessage()
    {
        return "Balance: " + ToXString(GetBalance()) + " " + GetCurrency();
    }

    //
    // END Provided Functions ...
    //

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Account Info ...
    CAccountInfo mAccountInfo;
};

//
// START Usefull Functions ...
//

//
// END Usefull Functions ...
//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-alert.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCAlert
// Description: provides all Alerts requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-data.collector.class.mq5"

//
// Class Definition ...

class XCAlert : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructor ...
    XCAlert(
        string prefix = "X-Alert",   // Alerts Prefixe
        bool enableAlerts = true,    // Enable Alerts
        bool logAlerts = true,       // Log Alerts
        bool terminalAlerts = false, // Terminal Alerts
        bool mailAlerts = false,     // Mail Alerts
        bool pushAlerts = false,     // Push Alerts
        bool saveAlerts = false      // Save Alerts
    )
    {
        //
        mPrefix = prefix;
        mLogAlerts = logAlerts;
        mMailAlerts = mailAlerts;
        mPushAlerts = pushAlerts;
        mSaveAlerts = saveAlerts;
        mEnableAlerts = enableAlerts;
        mTerminalAlerts = terminalAlerts;

        //
        // Normalize Prefix if Not Provided ...
        if (!IsValid(mPrefix))
        {
            mPrefix = GetTag();
        }

        //
        // Initialize Collector ...
        collector = new XCDataCollector();
        collector.Path(mPrefix);
    }

    //
    // Deconstructor ...
    ~XCAlert()
    {
        //
        // Cleanup Resources ...
        delete collector;
        ZeroMemory(collector);
    }

    //
    // Override ...
    string GetTag() override
    {
        return GetSpecificToken(this);
    }

    // #region Properties Getter(s) / Setter(s) ...
    //
    string GetPrefix()
    {
        return mPrefix;
    }

    //
    void SetPrefix(string value)
    {
        //
        mPrefix = value;
        collector.Path(mPrefix);
    }

    //
    bool GetLogAlerts()
    {
        return mLogAlerts;
    }

    //
    void SetLogAlerts(bool value)
    {
        mLogAlerts = value;
    }

    //
    bool GetEnableAlerts()
    {
        return mEnableAlerts;
    }

    //
    void SetEnableAlerts(bool value)
    {
        mEnableAlerts = value;
    }

    //
    bool GetSaveAlerts()
    {
        return mSaveAlerts;
    }

    //
    void SetSaveAlerts(bool value)
    {
        mSaveAlerts = value;
    }

    //
    bool GetPushAlerts()
    {
        return mPushAlerts;
    }

    //
    void SetPushAlerts(bool value)
    {
        mPushAlerts = value;
    }

    //
    bool GetMailAlerts()
    {
        return mMailAlerts;
    }

    //
    void SetMailAlerts(bool value)
    {
        mMailAlerts = value;
    }

    //
    bool GetTerminalAlerts()
    {
        return mTerminalAlerts;
    }

    //
    void SetTerminalAlerts(bool value)
    {
        mTerminalAlerts = value;
    }
    // #endregion

    // #region Tools Funtions ...

    //
    // Alerts ...

    //
    // Logging an Alert ...
    void LogAlert(string message, bool save = true)
    {
        //
        // Validate Args ...
        if (!CanLogAlert())
        {
            return;
        }

        //
        message = PrepareMessage(message);

        //
        Log(message);

        //
        if (save)
        {
            Save(message);
        }
    }

    //
    // Terminal Alert ...
    void TerminalAlert(string message, bool save = true)
    {
        //
        // Validate Args ...
        if (!CanTerminalAlert())
        {
            return;
        }

        //
        // Prepare Message ...
        message = PrepareMessage(message);

        // Send Terminal Alert ...
        Alert(message);

        //
        if (save)
        {
            Save(message);
        }
    }

    //
    // Mail Alert ...
    void MailAlert(string message, bool save = true)
    {
        //
        // Validate Args ...
        if (!CanMailAlert())
        {
            return;
        }

        //
        // Send Mail Alert ...
        SendMail(GetPrefix(), message);

        //
        if (save)
        {
            Save(message);
        }
    }

    //
    // Send Push Notification ...
    void PushAlert(string message, bool save = true)
    {
        //
        // Validate Args ...
        if (!CanPushAlert())
        {
            return;
        }

        //
        // Prepare Message ...
        message = PrepareMessage(message);

        //
        // Send Push Notification Alert ...
        SendNotification(message);

        //
        if (save)
        {
            Save(message);
        }
    }

    //
    // Send an Alert by Handling all Different Specified
    // type's of Alert's if they are Enabled ...
    void SendAlert(string message)
    {
        //
        // Handle Log, if Enabled ...
        LogAlert(message, false);

        //
        // Handle Mail if Enabled ...
        MailAlert(message, false);

        //
        // Handle Push if Enabled ...
        PushAlert(message, false);

        //
        // Handle Terminal if Enabled ...
        TerminalAlert(message, false);

        //
        if (GetEnableAlerts())
        {
            Save(message);
        }
    }

    //
    // Logs ...

    //
    // Log a Message ...
    void Log(string message)
    {
        //
        if (!CanLog())
        {
            return;
        }

        //
        Print(message);
    }
    void Log(string tag, string message)
    {
        //
        if (!CanLog())
        {
            return;
        }

        //
        Print(GetPrefix(), "_", tag, " > ", message);
    }
    void LogEmpty()
    {
        //
        if (!CanLog())
        {
            return;
        }

        //
        Print(" ");
    }

    //
    // Log Specific Array ...
    template <typename T>
    void LogArray(
        T &array[],         // Array to Log
        string label = NULL // Array Label
    )
    {
        //
        if (!CanLog())
        {
            return;
        }

        //
        Print(GetPrefix(), ">", (IsValid(label) ? label : ""));
        ArrayPrint(array);
    }

    //
    // Error ...

    //
    // Log an Error ...
    void LogError(
        int error = -1 // Specific Error, if it's equal to -1, use Last Error ...
    )
    {
        //
        if (!CanLog())
        {
            return;
        }

        //
        if (error <= -1)
        {
            error = GetLastError();
        }

        //
        string errorMsg = GetErrorDescription(error);

        //
        Log("Error: " + errorMsg);
    }

    //
    // Retrieve Specified Error Code Description ...
    string GetErrorDescription(int err_code)
    {
        //
        string result = "";

        //
        switch (err_code)
        {
            //
            //--- Constant Description

            //
        case ERR_SUCCESS:
            result = "The operation completed successfully";
            break;

            //
        case ERR_INTERNAL_ERROR:
            result = "Unexpected internal error";
            break;

            //
        case ERR_WRONG_INTERNAL_PARAMETER:
            result = "Wrong parameter in the inner call of the client terminal function";
            break;

            //
        case ERR_INVALID_PARAMETER:
            result = "Wrong parameter when calling the system function";
            break;

            //
        case ERR_NOT_ENOUGH_MEMORY:
            result = "Not enough memory to perform the system function";
            break;

            //
        case ERR_STRUCT_WITHOBJECTS_ORCLASS:
            result = "The structure contains objects of strings and/or dynamic arrays and/or structure of such objects and/or classes";
            break;

            //
        case ERR_INVALID_ARRAY:
            result = "Array of a wrong type, wrong size, or a damaged object of a dynamic array";
            break;

            //
        case ERR_ARRAY_RESIZE_ERROR:
            result = "Not enough memory for the relocation of an array, or an attempt to change the size of a static array";
            break;

            //
        case ERR_STRING_RESIZE_ERROR:
            result = "Not enough memory for the relocation of string";
            break;

            //
        case ERR_NOTINITIALIZED_STRING:
            result = "Not initialized string";
            break;

            //
        case ERR_INVALID_DATETIME:
            result = "Invalid date and/or time";
            break;

            //
        case ERR_ARRAY_BAD_SIZE:
            result = "Requested array size exceeds 2 GB";
            break;

            //
        case ERR_INVALID_POINTER:
            result = "Wrong pointer";
            break;

            //
        case ERR_INVALID_POINTER_TYPE:
            result = "Wrong type of pointer";
            break;

            //
        case ERR_FUNCTION_NOT_ALLOWED:
            result = "System function is not allowed to call";
            break;

            //
            //--- Charts

            //
        case ERR_CHART_WRONG_ID:
            result = "Wrong chart ID";
            break;

            //
        case ERR_CHART_NO_REPLY:
            result = "Chart does not respond";
            break;

            //
        case ERR_CHART_NOT_FOUND:
            result = "Chart not found";
            break;

            //
        case ERR_CHART_NO_EXPERT:
            result = "No Expert Advisor in the chart that could handle the event";
            break;

            //
        case ERR_CHART_CANNOT_OPEN:
            result = "Chart opening error";
            break;

            //
        case ERR_CHART_CANNOT_CHANGE:
            result = "Failed to change chart symbol and period";
            break;

            //
        case ERR_CHART_CANNOT_CREATE_TIMER:
            result = "Failed to create timer";
            break;

            //
        case ERR_CHART_WRONG_PROPERTY:
            result = "Wrong chart property ID";
            break;

            //
        case ERR_CHART_SCREENSHOT_FAILED:
            result = "Error creating screenshots";
            break;

            //
        case ERR_CHART_NAVIGATE_FAILED:
            result = "Error navigating through chart";
            break;

            //
        case ERR_CHART_TEMPLATE_FAILED:
            result = "Error applying template";
            break;

            //
        case ERR_CHART_WINDOW_NOT_FOUND:
            result = "Subwindow containing the indicator was not found";
            break;

            //
        case ERR_CHART_INDICATOR_CANNOT_ADD:
            result = "Error adding an indicator to chart";
            break;

            //
        case ERR_CHART_INDICATOR_CANNOT_DEL:
            result = "Error deleting an indicator from the chart";
            break;

            //
        case ERR_CHART_INDICATOR_NOT_FOUND:
            result = "Indicator not found on the specified chart";
            break;

            //
            //--- Graphical Objects
        case ERR_OBJECT_ERROR:
            result = "Error working with a graphical object";
            break;

            //
        case ERR_OBJECT_NOT_FOUND:
            result = "Graphical object was not found";
            break;

            //
        case ERR_OBJECT_WRONG_PROPERTY:
            result = "Wrong ID of a graphical object property";
            break;

            //
        case ERR_OBJECT_GETDATE_FAILED:
            result = "Unable to get date corresponding to the value";
            break;

            //
        case ERR_OBJECT_GETVALUE_FAILED:
            result = "Unable to get value corresponding to the date";
            break;

            //
            //--- MarketInfo

            //
        case ERR_MARKET_UNKNOWN_SYMBOL:
            result = "Unknown symbol";
            break;

            //
        case ERR_MARKET_NOT_SELECTED:
            result = "Symbol is not selected in MarketWatch";
            break;

            //
        case ERR_MARKET_WRONG_PROPERTY:
            result = "Wrong identifier of a symbol property";
            break;

            //
        case ERR_MARKET_LASTTIME_UNKNOWN:
            result = "Time of the last tick is not known (no ticks)";
            break;

            //
        case ERR_MARKET_SELECT_ERROR:
            result = "Error adding or deleting a symbol in MarketWatch";
            break;

            //
            //--- History Access

            //
        case ERR_HISTORY_NOT_FOUND:
            result = "Requested history not found";
            break;

            //
        case ERR_HISTORY_WRONG_PROPERTY:
            result = "Wrong ID of the history property";
            break;

            //
            //--- Global_Variables

            //
        case ERR_GLOBALVARIABLE_NOT_FOUND:
            result = "Global variable of the client terminal is not found";
            break;

            //
        case ERR_GLOBALVARIABLE_EXISTS:
            result = "Global variable of the client terminal with the same name already exists";
            break;

            //
        case ERR_MAIL_SEND_FAILED:
            result = "Email sending failed";
            break;

            //
        case ERR_PLAY_SOUND_FAILED:
            result = "Sound playing failed";
            break;

            //
        case ERR_MQL5_WRONG_PROPERTY:
            result = "Wrong identifier of the program property";
            break;

            //
        case ERR_TERMINAL_WRONG_PROPERTY:
            result = "Wrong identifier of the terminal property";
            break;

            //
        case ERR_FTP_SEND_FAILED:
            result = "File sending via ftp failed";
            break;

            //
        case ERR_NOTIFICATION_SEND_FAILED:
            result = "Error in sending notification";
            break;

            //
            //--- Custom Indicator Buffers

            //
        case ERR_BUFFERS_NO_MEMORY:
            result = "Not enough memory for the distribution of indicator buffers";
            break;

            //
        case ERR_BUFFERS_WRONG_INDEX:
            result = "Wrong indicator buffer index";
            break;

            //
            //--- Custom Indicator Properties

            //
        case ERR_CUSTOM_WRONG_PROPERTY:
            result = "Wrong ID of the custom indicator property";
            break;

            //
            //--- Account

            //
        case ERR_ACCOUNT_WRONG_PROPERTY:
            result = "Wrong account property ID";
            break;

            //
        case ERR_TRADE_WRONG_PROPERTY:
            result = "Wrong trade property ID";
            break;

            //
        case ERR_TRADE_DISABLED:
            result = "Trading by Expert Advisors prohibited";
            break;

            //
        case ERR_TRADE_POSITION_NOT_FOUND:
            result = "Position not found";
            break;

            //
        case ERR_TRADE_ORDER_NOT_FOUND:
            result = "Order not found";
            break;

            //
        case ERR_TRADE_DEAL_NOT_FOUND:
            result = "Deal not found";
            break;

            //
        case ERR_TRADE_SEND_FAILED:
            result = "Trade request sending failed";
            break;

            //
            //--- Indicators

            //
        case ERR_INDICATOR_UNKNOWN_SYMBOL:
            result = "Unknown symbol";
            break;

            //
        case ERR_INDICATOR_CANNOT_CREATE:
            result = "Indicator cannot be created";
            break;

            //
        case ERR_INDICATOR_NO_MEMORY:
            result = "Not enough memory to add the indicator";
            break;

            //
        case ERR_INDICATOR_CANNOT_APPLY:
            result = "The indicator cannot be applied to another indicator";
            break;

            //
        case ERR_INDICATOR_CANNOT_ADD:
            result = "Error applying an indicator to chart";
            break;

            //
        case ERR_INDICATOR_DATA_NOT_FOUND:
            result = "Requested data not found";
            break;

            //
        case ERR_INDICATOR_WRONG_HANDLE:
            result = "Wrong indicator handle";
            break;

            //
        case ERR_INDICATOR_WRONG_PARAMETERS:
            result = "Wrong number of parameters when creating an indicator";
            break;

            //
        case ERR_INDICATOR_PARAMETERS_MISSING:
            result = "No parameters when creating an indicator";
            break;

            //
        case ERR_INDICATOR_CUSTOM_NAME:
            result = "The first parameter in the array must be the name of the custom indicator";
            break;

            //
        case ERR_INDICATOR_PARAMETER_TYPE:
            result = "Invalid parameter type in the array when creating an indicator";
            break;

            //
        case ERR_INDICATOR_WRONG_INDEX:
            result = "Wrong index of the requested indicator buffer";
            break;

            //
            //--- Depth of Market

            //
        case ERR_BOOKS_CANNOT_ADD:
            result = "Depth Of Market can not be added";
            break;

            //
        case ERR_BOOKS_CANNOT_DELETE:
            result = "Depth Of Market can not be removed";
            break;

            //
        case ERR_BOOKS_CANNOT_GET:
            result = "The data from Depth Of Market can not be obtained";
            break;

            //
        case ERR_BOOKS_CANNOT_SUBSCRIBE:
            result = "Error in subscribing to receive new data from Depth Of Market";
            break;

            //
            //--- File Operations

            //
        case ERR_TOO_MANY_FILES:
            result = "More than 64 files cannot be opened at the same time";
            break;

            //
        case ERR_WRONG_FILENAME:
            result = "Invalid file name";
            break;

            //
        case ERR_TOO_LONG_FILENAME:
            result = "Too long file name";
            break;

            //
        case ERR_CANNOT_OPEN_FILE:
            result = "File opening error";
            break;

            //
        case ERR_FILE_CACHEBUFFER_ERROR:
            result = "Not enough memory for cache to read";
            break;

            //
        case ERR_CANNOT_DELETE_FILE:
            result = "File deleting error";
            break;

            //
        case ERR_INVALID_FILEHANDLE:
            result = "A file with this handle was closed, or was not opening at all";
            break;

            //
        case ERR_WRONG_FILEHANDLE:
            result = "Wrong file handle";
            break;

            //
        case ERR_FILE_NOTTOWRITE:
            result = "The file must be opened for writing";
            break;

            //
        case ERR_FILE_NOTTOREAD:
            result = "The file must be opened for reading";
            break;

            //
        case ERR_FILE_NOTBIN:
            result = "The file must be opened as a binary one";
            break;

            //
        case ERR_FILE_NOTTXT:
            result = "The file must be opened as a text";
            break;

            //
        case ERR_FILE_NOTTXTORCSV:
            result = "The file must be opened as a text or CSV";
            break;

            //
        case ERR_FILE_NOTCSV:
            result = "The file must be opened as CSV";
            break;

            //
        case ERR_FILE_READERROR:
            result = "File reading error";
            break;

            //
        case ERR_FILE_BINSTRINGSIZE:
            result = "String size must be specified, because the file is opened as binary";
            break;

            //
        case ERR_INCOMPATIBLE_FILE:
            result = "A text file must be for string arrays, for other arrays - binary";
            break;

            //
        case ERR_FILE_IS_DIRECTORY:
            result = "This is not a file, this is a directory";
            break;

            //
        case ERR_FILE_NOT_EXIST:
            result = "File does not exist";
            break;

            //
        case ERR_FILE_CANNOT_REWRITE:
            result = "File can not be rewritten";
            break;

            //
        case ERR_WRONG_DIRECTORYNAME:
            result = "Wrong directory name";
            break;

            //
        case ERR_DIRECTORY_NOT_EXIST:
            result = "Directory does not exist";
            break;

            //
        case ERR_FILE_ISNOT_DIRECTORY:
            result = "This is a file, not a directory";
            break;

            //
        case ERR_CANNOT_DELETE_DIRECTORY:
            result = "The directory cannot be removed";
            break;

            //
        case ERR_CANNOT_CLEAN_DIRECTORY:
            result = "Failed to clear the directory (probably one or more files are blocked and removal operation failed)";
            break;

            //
        case ERR_FILE_WRITEERROR:
            result = "Failed to write a resource to a file";
            break;

            //
            //--- String Casting

            //
        case ERR_NO_STRING_DATE:
            result = "No date in the string";
            break;

            //
        case ERR_WRONG_STRING_DATE:
            result = "Wrong date in the string";
            break;

            //
        case ERR_WRONG_STRING_TIME:
            result = "Wrong time in the string";
            break;

            //
        case ERR_STRING_TIME_ERROR:
            result = "Error converting string to date";
            break;

            //
        case ERR_STRING_OUT_OF_MEMORY:
            result = "Not enough memory for the string";
            break;

            //
        case ERR_STRING_SMALL_LEN:
            result = "The string length is less than expected";
            break;

            //
        case ERR_STRING_TOO_BIGNUMBER:
            result = "Too large number, more than ULONG_MAX";
            break;

            //
        case ERR_WRONG_FORMATSTRING:
            result = "Invalid format string";
            break;

            //
        case ERR_TOO_MANY_FORMATTERS:
            result = "Amount of format specifiers more than the parameters";
            break;

            //
        case ERR_TOO_MANY_PARAMETERS:
            result = "Amount of parameters more than the format specifiers";
            break;

            //
        case ERR_WRONG_STRING_PARAMETER:
            result = "Damaged parameter of string type";
            break;

            //
        case ERR_STRINGPOS_OUTOFRANGE:
            result = "Position outside the string";
            break;

            //
        case ERR_STRING_ZEROADDED:
            result = "0 added to the string end, a useless operation";
            break;

            //
        case ERR_STRING_UNKNOWNTYPE:
            result = "Unknown data type when converting to a string";
            break;

            //
        case ERR_WRONG_STRING_OBJECT:
            result = "Damaged string object";
            break;

            //
            //--- Operations with Arrays

            //
        case ERR_INCOMPATIBLE_ARRAYS:
            result = "Copying incompatible arrays. String array can be copied only to a string array, and a numeric array - in numeric array only";
            break;

            //
        case ERR_SMALL_ASSERIES_ARRAY:
            result = "The receiving array is declared as AS_SERIES, and it is of insufficient size";
            break;

            //
        case ERR_SMALL_ARRAY:
            result = "Too small array, the starting position is outside the array";
            break;

            //
        case ERR_ZEROSIZE_ARRAY:
            result = "An array of zero length";
            break;

            //
        case ERR_NUMBER_ARRAYS_ONLY:
            result = "Must be a numeric array";
            break;

            //
        case ERR_ONEDIM_ARRAYS_ONLY:
            result = "Must be a one-dimensional array";
            break;

            //
        case ERR_SERIES_ARRAY:
            result = "Timeseries cannot be used";
            break;

            //
        case ERR_DOUBLE_ARRAY_ONLY:
            result = "Must be an array of type double";
            break;

            //
        case ERR_FLOAT_ARRAY_ONLY:
            result = "Must be an array of type float";
            break;

            //
        case ERR_LONG_ARRAY_ONLY:
            result = "Must be an array of type long";
            break;

            //
        case ERR_INT_ARRAY_ONLY:
            result = "Must be an array of type int";
            break;

            //
        case ERR_SHORT_ARRAY_ONLY:
            result = "Must be an array of type short";
            break;

            //
        case ERR_CHAR_ARRAY_ONLY:
            result = "Must be an array of type char";
            break;

            //
            //--- Operations with OpenCL

            //
        case ERR_OPENCL_NOT_SUPPORTED:
            result = "OpenCL functions are not supported on this computer";
            break;

            //
        case ERR_OPENCL_INTERNAL:
            result = "Internal error occurred when running OpenCL";
            break;

            //
        case ERR_OPENCL_INVALID_HANDLE:
            result = "Invalid OpenCL handle";
            break;

            //
        case ERR_OPENCL_CONTEXT_CREATE:
            result = "Error creating the OpenCL context";
            break;

            //
        case ERR_OPENCL_QUEUE_CREATE:
            result = "Failed to create a run queue in OpenCL";
            break;

            //
        case ERR_OPENCL_PROGRAM_CREATE:
            result = "Error occurred when compiling an OpenCL program";
            break;

            //
        case ERR_OPENCL_TOO_LONG_KERNEL_NAME:
            result = "Too long kernel name (OpenCL kernel)";
            break;

            //
        case ERR_OPENCL_KERNEL_CREATE:
            result = "Error creating an OpenCL kernel";
            break;

            //
        case ERR_OPENCL_SET_KERNEL_PARAMETER:
            result = "Error occurred when setting parameters for the OpenCL kernel";
            break;

            //
        case ERR_OPENCL_EXECUTE:
            result = "OpenCL program runtime error";
            break;

            //
        case ERR_OPENCL_WRONG_BUFFER_SIZE:
            result = "Invalid size of the OpenCL buffer";
            break;

            //
        case ERR_OPENCL_WRONG_BUFFER_OFFSET:
            result = "Invalid offset in the OpenCL buffer";
            break;

            //
        case ERR_OPENCL_BUFFER_CREATE:
            result = "Failed to create and OpenCL buffer";
            break;

            //
            //--- User-Defined Errors
        default:
            result = "UNKNOWN Erro ...";
            break;
        }

        //
        return result;
    }

    //
    // Retrieve Specified Error Code Description ...
    string GetTradeServerReturnCodeDescription(int return_code)
    {
        //
        string result = "";

        //
        switch (return_code)
        {
            //
        case TRADE_RETCODE_REQUOTE:
            result = "Requote";
            break;

            //
        case TRADE_RETCODE_REJECT:
            result = "Request rejected";
            break;

            //
        case TRADE_RETCODE_CANCEL:
            result = "Request canceled by trader";
            break;

            //
        case TRADE_RETCODE_PLACED:
            result = "Order placed";
            break;

            //
        case TRADE_RETCODE_DONE:
            result = "Request completed";
            break;

            //
        case TRADE_RETCODE_DONE_PARTIAL:
            result = "Only part of the request was completed";
            break;

            //
        case TRADE_RETCODE_ERROR:
            result = "Request processing error";
            break;

            //
        case TRADE_RETCODE_TIMEOUT:
            result = "Request canceled by timeout";
            break;

            //
        case TRADE_RETCODE_INVALID:
            result = "Invalid request";
            break;

            //
        case TRADE_RETCODE_INVALID_VOLUME:
            result = "Invalid volume in the request";
            break;

            //
        case TRADE_RETCODE_INVALID_PRICE:
            result = "Invalid price in the request";
            break;

            //
        case TRADE_RETCODE_INVALID_STOPS:
            result = "Invalid stops in the request";
            break;

            //
        case TRADE_RETCODE_TRADE_DISABLED:
            result = "Trade is disabled";
            break;

            //
        case TRADE_RETCODE_MARKET_CLOSED:
            result = "Market is closed";
            break;

            //
        case TRADE_RETCODE_NO_MONEY:
            result = "There is not enough money to complete the request";
            break;

            //
        case TRADE_RETCODE_PRICE_CHANGED:
            result = "Prices changed";
            break;

            //
        case TRADE_RETCODE_PRICE_OFF:
            result = "There are no quotes to process the request";
            break;

            //
        case TRADE_RETCODE_INVALID_EXPIRATION:
            result = "Invalid order expiration date in the request";
            break;

            //
        case TRADE_RETCODE_ORDER_CHANGED:
            result = "Order state changed";
            break;

            //
        case TRADE_RETCODE_TOO_MANY_REQUESTS:
            result = "Too frequent requests";
            break;

            //
        case TRADE_RETCODE_NO_CHANGES:
            result = "No changes in request";
            break;

            //
        case TRADE_RETCODE_SERVER_DISABLES_AT:
            result = "Autotrading disabled by server";
            break;

            //
        case TRADE_RETCODE_CLIENT_DISABLES_AT:
            result = "Autotrading disabled by client terminal";
            break;

            //
        case TRADE_RETCODE_LOCKED:
            result = "Request locked for processing";
            break;

            //
        case TRADE_RETCODE_FROZEN:
            result = "Order or position frozen";
            break;

            //
        case TRADE_RETCODE_INVALID_FILL:
            result = "Invalid order filling type";
            break;

            //
        case TRADE_RETCODE_CONNECTION:
            result = "No connection with the trade server";
            break;

            //
        case TRADE_RETCODE_ONLY_REAL:
            result = "Operation is allowed only for live accounts";
            break;

            //
        case TRADE_RETCODE_LIMIT_ORDERS:
            result = "The number of pending orders has reached the limit";
            break;

            //
        case TRADE_RETCODE_LIMIT_VOLUME:
            result = "The volume of orders and positions for the symbol has reached the limit";
            break;

        //
        default:
            result = "Invalid return code of the trade server";
            break;
        }

        //
        return result;
    }
    // #endregion

    //
    // Protected ...
  protected:
    //
    // Private ...

    //
    bool CanLog()
    {
        return mLogAlerts;
    }

    //
    bool CanLogAlert()
    {
        //
        bool result = GetEnableAlerts() && GetLogAlerts();

        //
        return result;
    }

    //
    bool CanTerminalAlert()
    {
        //
        bool result = GetEnableAlerts() && GetTerminalAlerts();

        //
        return result;
    }

    //
    bool CanMailAlert()
    {
        //
        bool result = GetEnableAlerts() && GetMailAlerts();

        //
        return result;
    }

    //
    bool CanPushAlert()
    {
        //
        bool result = GetEnableAlerts() && GetPushAlerts();

        //
        return result;
    }

    //
    void Save(string message)
    {
        //
        if (!mSaveAlerts ||
            !IsValid(message) ||
            message == mLastSavedMessage ||
            PrepareMessage(message) == mLastSavedMessage)
        {
            return;
        }

        //
        string fileName = mPrefix + "." + "XLogs";
        string filePath = collector.GetFilePath(fileName);
        if (!IsValid(filePath))
        {
            return;
        }

        //
        // Attach Time ...
        string savedMessage = ToXString(TimeCurrent()) + " " + message;

        //
        bool has = collector.Append(
            filePath,
            savedMessage //
        );
        if (has)
        {
            mLastSavedMessage = message;
        }
    }

  private:
    //
    // Props ...
    string mPrefix;       // Alerts Prefixe
    bool mEnableAlerts;   // Enable Alerts
    bool mSaveAlerts;     // Save Alerts
    bool mLogAlerts;      // Log Alerts
    bool mTerminalAlerts; // Terminal Alerts
    bool mMailAlerts;     // Mail Alerts
    bool mPushAlerts;     // Push Alerts

    //
    string mLastSavedMessage; // For Prevent Multiple Saving Same Mesage ...
    XCDataCollector *collector;

    //
    // Tools ...

    //
    // Clear Escape String and
    // Attach Alert Prefix to Message ...
    string PrepareMessage(string message)
    {
        //
        StringReplace(message, "\n", " | ");

        //
        StringConcatenate(message, GetPrefix(), " > ", message);

        //
        return message;
    }
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-app-dialog.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCPanel
// Description: provides all based classes for use ...
//
//
// 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

//
// Imports ...

//
#include <Controls/Dialog.mqh>

//
#include "../Classes/x-saherelm.base.class.mq5"

//
// Extentions ...

/**
 * Check a Qery Contains in a Content or not ...
 *
 * @param  mQuery: String ...
 * @param  mContent: String ...
 * @param  ignoreCase: Boolean ...
 *
 * @return ( bool )
 */
bool XContains(
    string mQuery,         // Search String
    string mContent,       // Search Content
    bool ignoreCase = true // Ignore Case
)
{
    return Contains(
        mQuery,
        mContent,
        ignoreCase //
    );
}

//
// Implementation ...

//
// a Base App Dialog Class ...
class XCAppDialog : public CAppDialog
{
  public:
    //
    // Action ...

    //
    // Virual ...

    /**
     * Override Close Button Click Action ...
     */
    void OnClickButtonClose()
    {
        //
        bool isClosed = ConfirmDialog("Close EA ?");

        //
        if (isClosed)
        {
            Destroy();
        }
    }

    //
    // Configuration Actions ...

    /**
     * Show or Hide Minimize Button ...
     *
     * @param  show: boolean
     */
    void MinimizeButton(bool show)
    {
        //
        int controlIDX = ControlFind("MinMax");
        if (!IsValidIndex(controlIDX))
        {
            return;
        }

        //
        if (show)
        {
            Control(controlIDX).Show();
        }
        else
        {
            Control(controlIDX).Hide();
        }
    }

    /**
     * Show or Hide Close Button ...
     *
     * @param  show: boolean
     */
    void CloseButton(bool show)
    {
        //
        int controlIDX = ControlFind("Close");
        if (!IsValidIndex(controlIDX))
        {
            return;
        }

        //
        if (show)
        {
            Control(controlIDX).Show();
        }
        else
        {
            Control(controlIDX).Hide();
        }
    }

    /**
     * Minimize Functionality Implementation ...
     */
    void Minimize()
    {
        //
        m_minimized = true;
        Rebound(m_min_rect);
        ClientAreaVisible(false);
    }

    /**
     * Set Background Color ...
     *
     * @param  clr: Color ...
     */
    void BackgroundColor(color clr)
    {
        //
        int controlIDX = ControlFind("Client");
        if (!IsValidIndex(controlIDX))
        {
            return;
        }

        //
        CWnd *obj = Control(controlIDX);
        CWndClient *wndclient = (CWndClient *)obj;

        //
        wndclient.ColorBackground(clr);
    }

    //
    // Overrides ...

    //
  protected:
    //

    /**
     * Find Specific Control in Dialog ...
     *
     * @param  name: String to Search
     *
     * @return ( int )
     */
    int ControlFind(string name)
    {
        //
        int result = -1;

        //
        if (!IsValid(name))
        {
            return result;
        }

        //
        int count = ControlsTotal();
        if (!IsValidSize(count))
        {
            return result;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            string iName = Control(i).Name();

            //
            bool isContains = XContains(
                name,
                iName,
                true // Ignore Case ...
            );
            if (isContains)
            {
                //
                result = i;
                break;
            }
        }

        //
        return result;
    }

    /**
     * Create and Run Confirm Dialog ...
     *
     * @param  message: message for Message Box ...
     *
     * @return ( bool )
     */
    bool ConfirmDialog(string message = NULL)
    {
        //
        bool result = false;

        //
        if (!IsValid(message))
        {
            message = "Confirm ?";
        }

        //
        int dialogResult = MessageBox(message, NULL, MB_YESNO);

        //
        // use 'switch' or 'if' as needed
        switch (dialogResult)
        {
            //
            // Yes ...
        case IDYES:
            result = true;
            break;
            //
            // No ...
        case IDNO:
            result = false;
            break;
            //
            // Cancel ...
        case IDCANCEL:
            result = false;
            break;
        }

        //
        return result;
    }

    //
  private:
    //

    //
};

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-backtester.expert.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCBackTesterEA
// Description: Back Tester Expert Class ...
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Classes/x-saherelm.x-chart.helper.class.mq5"
#include "../Classes/x-saherelm.x-guard.class.mq5"
#include "../Classes/x-saherelm.x-position.drawer.class.mq5"
#include "../Classes/x-saherelm.x-target.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Classes/x-saherelm.x-volume.class.mq5"

//
// Import Chart Objects ...
#include <Arrays/ArrayObj.mqh>
#include <ChartObjects/ChartObject.mqh>
#include <ChartObjects/ChartObjectsArrows.mqh>
#include <ChartObjects/ChartObjectsFibo.mqh>
#include <ChartObjects/ChartObjectsLines.mqh>
#include <ChartObjects/ChartObjectsShapes.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>
#include <Charts/Chart.mqh>
#include <Controls/Button.mqh>
#include <Controls/Edit.mqh>
#include <Controls/Label.mqh>

//
// Definitions ...

//
// Implementation ...
class XCBackTesterEA : public XCBase
{
    //
    // Public ...
  public:
    //

    //
    // Common ...
    long eaMagicNumber; // Magic Number
    int eaSlippage;     // Slippgae
    string eaLogSuffix; // Log Suffix

    //
    // Symbol / Period ...
    string symbol;          // Symbol
    ENUM_TIMEFRAMES period; // Period

    //
    // Volume ...
    ENUM_X_VOLUME_SELECT_TYPE eaVolumeSelect; // Volume Applying Type
    double eaStaticVoluem;                    // Static Volume
    double eaDynamicVolumeStepBalance;        // Step of Balance for Increase Volume
    double eaDynamicVolumeStepVolume;         // Step of Volume Increasing
    double eaConstantRiskBalance;             // Constant Risk Balance per Trade
    double eaConstantPercent;                 // Constant Percent of Balance Per Trade
    double eaConstantBalance;                 // Constant Balance for Calculations

    //
    // Alert ...
    bool eaEnableAlerts;   // Enable Alerts
    bool eaLogAlerts;      // Log Alerts
    bool eaMailAlerts;     // Mail Alerts
    bool eaPushAlerts;     // Push Alerts
    bool eaTerminalAlerts; // Terminal Alerts

    //
    // SL/TP Manipulation Props ...
    double allowedTPSLStep;       // Value for Change TP/SL in Each Step ...
    double minAllowedRiskInPoint; // Min Allowed Risk in Point ...
    double riskInPoint;           // Risk in Point per Trades ...
    double riskToRewardRatio;     // Risk To Reward per Trades ...
    double volume;                // Default volume Per Trade ...

    //
    XCAlert *eaAlert;                   // EA Scope Alert Handler ...
    XCGuard *eaGuard;                   // EA Scope Guard Handler ...
    XCTrade *eaTrader;                  // EA Scope Trade Handler ...
    XCTarget *eaTarget;                 // EA Scope Target Manager ...
    XCVolume *eaVolume;                 // EA Scope Volume Manager ...
    XCChartHelper *eaChartHelper;       // EA Chart Helper ...
    XCPositionDrawer *eaPositionDrawer; // EA Scope Position Drawer ...

    //
    // Constructor ...
    XCBackTesterEA()
    {
        //
        selectedPositionID = 0;

        //
        volume = 0.01;
        riskInPoint = 50;
        allowedTPSLStep = 5;
        riskToRewardRatio = 2;
        minAllowedRiskInPoint = 10;
    }

    //
    // Deconstructor ...
    ~XCBackTesterEA()
    {
    }

    //
    // Actions / Handlers ...

    //
    // Expert Advisor Event Handlers ...

    /**
     * Handle Initialization of Expert ...
     *
     * @return ( bool )
     */
    bool HandleOnInit()
    {
        //
        bool result = false;

        //
        // Validate Inputs ...
        result = ValidateInputs();
        if (!result)
        {
            return result;
        }

        //
        int count = 0;
        bool has = false;

        //
        // Chart Helper ...
        eaChartHelper = new XCChartHelper();

        //
        // Initialize Alert Handler ...
        eaAlert = new XCAlert();
        eaAlert.SetPrefix(eaLogSuffix);
        eaAlert.SetLogAlerts(eaLogAlerts);
        eaAlert.SetMailAlerts(eaMailAlerts);
        eaAlert.SetPushAlerts(eaPushAlerts);
        eaAlert.SetEnableAlerts(eaEnableAlerts);
        eaAlert.SetTerminalAlerts(eaTerminalAlerts);

        //
        // Initialize Trader ...
        eaTrader = new XCTrade(
            eaSlippage,
            eaMagicNumber,
            0,
            0,
            0 //
        );

        //
        // Trade Handler Attachments ...

        //
        // Position Modifiy Event Handlers ...
        count = ArraySize(mOnModifyEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnModifyPositionEventHandler(mOnModifyEventHandlers[i]);
            }
        }

        //
        // Position Stop Loss Event Handlers ...
        count = ArraySize(mStopLossEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnStopLossEventHandler(mStopLossEventHandlers[i]);
            }
        }

        //
        // Position Take Profit Event Handlers ...
        count = ArraySize(mTakeProfitEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnTakeProfitEventHandler(mTakeProfitEventHandlers[i]);
            }
        }

        //
        // Position Force Close Event Handlers ...
        count = ArraySize(mOnForceCloseEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnForceClosePositionEventHandler(mOnForceCloseEventHandlers[i]);
            }
        }

        //
        // Deals Changed Event Handlers ...
        count = ArraySize(mDealsChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnDealsChangedEventHandler(mDealsChangedEventHandlers[i]);
            }
        }

        //
        // Orders Changed Event Handlers ...
        count = ArraySize(mOrdersChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnOrdersChangedEventHandler(mOrdersChangedEventHandlers[i]);
            }
        }

        //
        // Positions Changed Event Handlers ...
        count = ArraySize(mPositionsChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnPositionsChangedEventHandler(mPositionsChangedEventHandlers[i]);
            }
        }

        //
        // Trade Changed Event Handlers ...
        count = ArraySize(mTradeStateChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnTradeStateChangedEventHandler(mTradeStateChangedEventHandlers[i]);
            }
        }

        //
        // Postion Partially Close Event Handlers ...
        count = ArraySize(mOnPartialCloseEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnPartialClosePositionEventHandler(mOnPartialCloseEventHandlers[i]);
            }
        }

        //
        // Signal Executed Event Handlers ...
        count = ArraySize(mOnSignalExecutedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnSignalExecutedEventHandler(mOnSignalExecutedEventHandlers[i]);
            }
        }

        //
        // Init Target Managers ...
        eaTarget = new XCTarget(eaTrader, eaAlert);

        //
        // Initialize Guard Handler ...
        eaGuard = new XCGuard(
            eaAlert,
            eaTrader //
        );

        //
        eaVolume = new XCVolume();
        result = eaVolume.Init(
            eaVolumeSelect,
            eaStaticVoluem,
            eaDynamicVolumeStepBalance,
            eaDynamicVolumeStepVolume,
            eaConstantRiskBalance,
            eaConstantPercent,
            eaConstantBalance //
        );
        if (!result)
        {
            return result;
        }

        //
        // Init Position Drawer ...
        eaPositionDrawer = new XCPositionDrawer(eaTrader);

        //
        // Initial GUI ...
        result = InitGUI();
        if (!result)
        {
            return result;
        }

        //
        // Normalize Requirements ...
        NormalizeProps();

        //
        return result;
    }

    /**
     * Handle De Initialization of Expert ...
     */
    void HandleOnDeInit()
    {
        //
        eaTarget.Destroy();
        delete eaTarget;
        ZeroMemory(eaTarget);

        //
        if (eaPositionDrawer != NULL)
        {
            //
            eaPositionDrawer.Destroy();
            delete eaPositionDrawer;
            ZeroMemory(eaPositionDrawer);
        }

        //
        delete eaVolume;
        ZeroMemory(eaVolume);

        //
        delete eaTrader;
        ZeroMemory(eaTrader);

        //
        delete eaGuard;
        ZeroMemory(eaGuard);

        //
        SpecifiedClean(mOnModifyEventHandlers);
        SpecifiedClean(mStopLossEventHandlers);
        SpecifiedClean(mTakeProfitEventHandlers);
        SpecifiedClean(mOnForceCloseEventHandlers);
        SpecifiedClean(mDealsChangedEventHandlers);
        SpecifiedClean(mOrdersChangedEventHandlers);
        SpecifiedClean(mOnPartialCloseEventHandlers);
        SpecifiedClean(mPositionsChangedEventHandlers);
        SpecifiedClean(mTradeStateChangedEventHandlers);

        //
        DestroyGUI();

        //
        string message = "DeInitialized Successfully ...";
        eaAlert.SendAlert(message);

        //
        delete eaAlert;
        ZeroMemory(eaAlert);
    }

    /**
     * Handle Tick ...
     */
    void HandleOnTick()
    {
        //
        UpdateGUI();

        //
        // Managing Targets ...
        eaTarget.Manage();

        //
        // Manage Positions Drawings ...
        if (eaPositionDrawer != NULL)
        {
            eaPositionDrawer.Update();
        }
    }

    /**
     * Handle Trade ...
     */
    void HandleOnTrade()
    {
        eaTrader.HandleOnTrade();
    }

    /**
     * Handle Timer ...
     */
    void HandleOnTimer()
    {
    }

    /**
     * Handle Chart Event ...
     *
     * @param id: int, Event id ...
     * @param lparam: long, Event Long Parameter ...
     * @param dparam: double, Event Double Parameter ...
     * @param sparam: string, Event String Parameter ...
     */
    void HandleOnChartEvent(
        const int id,
        const long &lparam,
        const double &dparam,
        const string &sparam //
    )
    {
    }

    //
    // Validators ...

    /**
     * Validate Inputs ...
     *
     * @return ( bool )
     */
    bool ValidateInputs()
    {
        //
        bool result = false;

        //
        // TODO: Handle this ...
        result = true;

        //
        return result;
    }

    //
    // Trade Event Handlers ...

    /**
     * Handle Stop Loss Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleOnStopLossTriggered(const XDeal &deal)
    {
        //
        // Update Position Buttons ...
        UpdatePositionButtons();

        //
        eaTarget.HandleOnStopLossTriggered(deal);

        //
        if (eaPositionDrawer != NULL)
        {
            eaPositionDrawer.HandleStopLossTriggered(deal);
        }
    }

    /**
     * Handle Take Profit Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleOnTakeProfitTriggered(const XDeal &deal)
    {
        //
        // Update Position Buttons ...
        UpdatePositionButtons();

        //
        eaTarget.HandleOnTakeProfitTriggered(deal);

        //
        if (eaPositionDrawer != NULL)
        {
            eaPositionDrawer.HandleOnTakeProfitTriggered(deal);
        }
    }

    /**
     * Handle Position Force Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param position: XPosition instance refrence, Triggered Position ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionForceClosed(
        const ulong ticket,
        const XPosition &position,
        const string comment //
    )
    {
        //
        // Update Position Buttons ...
        UpdatePositionButtons();

        //
        eaTarget.HandleOnPositionForceClosed(
            ticket,
            position,
            comment //
        );

        //
        if (eaPositionDrawer != NULL)
        {
            //
            eaPositionDrawer.HandleOnPositionForceClosed(
                ticket,
                position,
                comment //
            );
        }
    }

    /**
     * Handle Position Partially Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param profit: double, Position Profit ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionPartialClosed(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
    }

    /**
     * Handle Deals Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void HandleOnDealsChanged(int count)
    {
    }

    /**
     * Handle Orders Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void HandleOnOrdersChanged(int count)
    {
    }

    /**
     * Handle Positions Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void HandleOnPositionsChanged(int count)
    {
        //
        // Update Position Buttons ...
        UpdatePositionButtons();
    }

    /**
     * Handle Position Modified Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param profit: double, Position Profit ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionModified(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
        //
        if (eaPositionDrawer != NULL)
        {
            //
            eaPositionDrawer.HandleOnPositionModified(
                ticket,
                profit,
                comment //
            );
        }
    }

    /**
     * Handle all Trades States Changed Event ...
     *
     * @param state: XOnTradeHandlerState instance refrence, Changed Trade State data ...
     */
    void HandleOnTradeStateChanged(const XOnTradeHandlerState &state)
    {
    }

    /**
     * Handle New Signal Recieved ...
     *
     * @param  signal: XSignal, reference to recieved Signal ...
     */
    void HandleOnSignalTriggered(XSignal &signal)
    {
        //
        // Draw Signal ...
        XCSignalObject *iSignalObj = new XCSignalObject();
        bool isCreated = iSignalObj.Create(
            eaChartHelper.ChartId(),
            0,
            signal,
            3 //
        );
        if (isCreated)
        {
        }

        //
        // Handle Volume ...
        double iVolume = eaVolume.CalculateVolume(signal);
        if (!signal.ignoreEAVolume &&
            iVolume != signal.volume)
        {
            signal.volume = iVolume;
        }

        //
        ENUM_X_SIGNAL_EXECUTION_RESULT result;
        bool isExcuted = eaTrader.ExecuteSignal(
            signal,
            result //
        );
        if (isExcuted)
        {
        }
    }

    /**
     * Handle Signal Executed Event ...
     *
     * @param  signal: XSignal, reference to Executed Signal ...
     */
    void HandleOnSignalExecuted(XSignal &signal)
    {
        //
        eaTarget.AddExecutedSignal(signal);

        //
        if (eaPositionDrawer != NULL)
        {
            eaPositionDrawer.AddExecutedSignal(signal);
        }
    }

    //
    // Props and Setter / Getter (s) ...

    //
    // Trade Event Listeners ...

    //
    void AddOnStopLossEventHandler(TOnStopLoss handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mStopLossEventHandlers //
        );
    }

    //
    void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTakeProfitEventHandlers //
        );
    }

    //
    void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mDealsChangedEventHandlers //
        );
    }

    //
    void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOrdersChangedEventHandlers //
        );
    }

    //
    void AddOnModifyPositionEventHandler(TOnModify handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnModifyEventHandlers //
        );
    }

    //
    void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mPositionsChangedEventHandlers //
        );
    }

    //
    void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTradeStateChangedEventHandlers //
        );
    }

    //
    void AddOnForceClosePositionEventHandler(TOnForceClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnForceCloseEventHandlers //
        );
    }

    //
    void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnPartialCloseEventHandlers //
        );
    }

    //
    // Add New Signal Executed Event Handler ...
    void AddOnSignalExecutedEventHandler(TOnSignal handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnSignalExecutedEventHandlers //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...

    //
    // Trade Event Listeners ...
    TOnModify mOnModifyEventHandlers[];
    TOnStopLoss mStopLossEventHandlers[];
    TOnTakeProfit mTakeProfitEventHandlers[];
    TOnForceClose mOnForceCloseEventHandlers[];
    TOnSignal mOnSignalExecutedEventHandlers[];
    TOnDealsChanged mDealsChangedEventHandlers[];
    TOnOrdersChanged mOrdersChangedEventHandlers[];
    TOnPartialClose mOnPartialCloseEventHandlers[];
    TOnPositionsChanged mPositionsChangedEventHandlers[];
    TOnTradeStateChanged mTradeStateChangedEventHandlers[];

    //
    // Object Props ...

    //
    string objSuffix;

    //
    CButton btnBuy;
    CButton btnSell;
    CButton btnClose;
    CButton btnCloseAll;
    CEdit lblPositionId;

    //
    CButton lblTP;
    CButton lblSL;
    CButton btnSLPlus;
    CButton btnTPPlus;
    CButton btnSLMinus;
    CButton btnTPMinus;
    CButton btnSLPlusPlus;
    CButton btnTPPlusPlus;
    CButton btnSLMinusMinus;
    CButton btnTPMinusMinus;

    //
    CArrayObj posArray;
    CButton *btnSelectedPosInfo;
    CArrayObj selectedPosInfoArr;

    //
    ulong selectedPositionID;
    bool showSelectedPositionInfo;

    //
    // GUI Event(s) ...

    /**
     * Initialize all GUI Requirements for EA ...
     *
     * @return ( bool )
     */
    bool InitGUI()
    {
        //
        bool result = false;

        //
        int subWindow = 0;
        ulong chartId = ChartID();
        objSuffix = eaLogSuffix + "_";

        //
        int startX = 5;
        int startY = 25;

        //
        int defaultGap = 5;
        int defaultWidth = 100;
        int defaultHeight = 30;

        //
        // Buy ...
        int btnBuyX1 = startX;
        int btnBuyX2 = startX + defaultWidth;
        int btnBuyY1 = startY;
        int btnBuyY2 = startY + defaultHeight;
        string btnBuyName = objSuffix + "BTN_BUY";
        result = btnBuy.Create(
            chartId,
            btnBuyName,
            subWindow,
            btnBuyX1,
            btnBuyY1,
            btnBuyX2,
            btnBuyY2 //
        );
        if (result)
        {
            //
            //  Apply Buy Button Style ...

            //
            btnBuy.Text("Buy");
            btnBuy.Color(clrYellow);
            btnBuy.ColorBackground(clrGreen);
        }

        //
        // Sell ...
        int btnSellX1 = btnBuyX1 + btnBuyX2;
        int btnSellX2 = btnSellX1 + defaultWidth;
        int btnSellY1 = btnBuyY1;
        int btnSellY2 = btnBuyY2;
        string btnSellName = objSuffix + "BTN_SELL";
        result = btnSell.Create(
            chartId,
            btnSellName,
            subWindow,
            btnSellX1,
            btnSellY1,
            btnSellX2,
            btnSellY2 //
        );
        if (result)
        {
            //
            // Apply Sell Button Style ...

            //
            btnSell.Text("Sell");
            btnSell.Color(clrYellow);
            btnSell.ColorBackground(clrDarkRed);
        }

        //
        // LABEL Position ID ...
        int lblPositionIdX1 = btnBuyX1;
        int lblPositionIdX2 = lblPositionIdX1 + defaultWidth;
        int lblPositionIdY1 = btnSellY2 + defaultGap;
        int lblPositionIdY2 = lblPositionIdY1 + defaultHeight;
        string lblPositionIdName = objSuffix + "LBL_POS_ID";
        result = lblPositionId.Create(
            chartId,
            lblPositionIdName,
            subWindow,
            lblPositionIdX1,
            lblPositionIdY1,
            lblPositionIdX2,
            lblPositionIdY2 //
        );
        if (result)
        {
            //
            // Apply Label Position ID Style ...
            lblPositionId.ReadOnly(true);
            lblPositionId.Text("Pos ID: ");
            lblPositionId.Color(clrYellow);
            lblPositionId.ColorBackground(clrDarkGray);
        }

        //
        // Close ...
        int btnCloseX1 = startX;
        int btnCloseX2 = btnCloseX1 + defaultWidth;
        int btnCloseY1 = lblPositionIdY2 + defaultGap;
        int btnCloseY2 = btnCloseY1 + defaultHeight;
        string btnCloseName = objSuffix + "BTN_CLOSE";
        result = btnClose.Create(
            chartId,
            btnCloseName,
            subWindow,
            btnCloseX1,
            btnCloseY1,
            btnCloseX2,
            btnCloseY2 //
        );
        if (result)
        {
            //
            // Apply Close Button Style ...

            //
            btnClose.Text("Close");
            btnClose.Color(clrYellow);
            btnClose.ColorBackground(clrDarkOrange);
        }

        //
        // Close All ...
        int btnCloseAllX1 = btnCloseX2 + defaultGap;
        int btnCloseAllX2 = btnCloseAllX1 + defaultWidth;
        int btnCloseAllY1 = lblPositionIdY2 + defaultGap;
        int btnCloseAllY2 = btnCloseAllY1 + defaultHeight;
        string btnCloseAllName = objSuffix + "BTN_CLOSE_ALL";
        result = btnCloseAll.Create(
            chartId,
            btnCloseAllName,
            subWindow,
            btnCloseAllX1,
            btnCloseAllY1,
            btnCloseAllX2,
            btnCloseAllY2 //
        );
        if (result)
        {
            //
            // Apply Close Button Style ...

            //
            btnCloseAll.Text("Close All");
            btnCloseAll.Color(clrYellow);
            btnCloseAll.ColorBackground(clrDarkRed);
        }

        //
        int minusPlusWidth = 20;

        //
        // TP Label ...
        int lblTPX1 = btnCloseX1;
        int lblTPX2 = lblTPX1 + defaultWidth;
        int lblTPY1 = btnCloseY2 + defaultGap;
        int lblTPY2 = lblTPY1 + defaultHeight;
        string lblTPName = objSuffix + "LBL_TP";
        result = lblTP.Create(
            chartId,
            lblTPName,
            subWindow,
            lblTPX1,
            lblTPY1,
            lblTPX2,
            lblTPY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            lblTP.Disable();
            lblTP.Text("TP");
            lblTP.Color(clrWhite);
            lblTP.ColorBackground(clrGreen);
        }

        //
        // TP Plus ...
        int btnTPPlusX1 = lblTPX1;
        int btnTPPlusX2 = btnTPPlusX1 + minusPlusWidth;
        int btnTPPlusY1 = lblTPY2 + defaultGap;
        int btnTPPlusY2 = btnTPPlusY1 + defaultHeight;
        string btnTPPlusName = objSuffix + "BTN_TP_PLUS";
        result = btnTPPlus.Create(
            chartId,
            btnTPPlusName,
            subWindow,
            btnTPPlusX1,
            btnTPPlusY1,
            btnTPPlusX2,
            btnTPPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPPlus.Text("+");
            btnTPPlus.Color(clrYellow);
            btnTPPlus.ColorBackground(clrGreen);
        }

        //
        // TP Plus  Plus ...
        int btnTPPlusPlusX1 = btnTPPlusX2 + defaultGap;
        int btnTPPlusPlusX2 = btnTPPlusPlusX1 + minusPlusWidth;
        int btnTPPlusPlusY1 = lblTPY2 + defaultGap;
        int btnTPPlusPlusY2 = btnTPPlusPlusY1 + defaultHeight;
        string btnTPPlusPlusName = objSuffix + "BTN_TP_PLUSPLUS";
        result = btnTPPlusPlus.Create(
            chartId,
            btnTPPlusPlusName,
            subWindow,
            btnTPPlusPlusX1,
            btnTPPlusPlusY1,
            btnTPPlusPlusX2,
            btnTPPlusPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPPlusPlus.Text("++");
            btnTPPlusPlus.Color(clrYellow);
            btnTPPlusPlus.ColorBackground(clrGreen);
        }

        //
        // TP Minus ...
        int btnTPMinusX1 = lblTPX2 - minusPlusWidth;
        int btnTPMinusX2 = btnTPMinusX1 + minusPlusWidth;
        int btnTPMinusY1 = lblTPY2 + defaultGap;
        int btnTPMinusY2 = btnTPMinusY1 + defaultHeight;
        string btnTPMinusName = objSuffix + "BTN_TP_MINUS";
        result = btnTPMinus.Create(
            chartId,
            btnTPMinusName,
            subWindow,
            btnTPMinusX1,
            btnTPMinusY1,
            btnTPMinusX2,
            btnTPMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPMinus.Text("-");
            btnTPMinus.Color(clrYellow);
            btnTPMinus.ColorBackground(clrDarkRed);
        }

        //
        // TP Minus Minus ...
        int btnTPMinusMinusX1 = btnTPMinusX1 - minusPlusWidth - defaultGap;
        int btnTPMinusMinusX2 = btnTPMinusMinusX1 + minusPlusWidth;
        int btnTPMinusMinusY1 = lblTPY2 + defaultGap;
        int btnTPMinusMinusY2 = btnTPMinusY1 + defaultHeight;
        string btnTPMinusMinusName = objSuffix + "BTN_TP_MINUSMINUS";
        result = btnTPMinusMinus.Create(
            chartId,
            btnTPMinusMinusName,
            subWindow,
            btnTPMinusMinusX1,
            btnTPMinusMinusY1,
            btnTPMinusMinusX2,
            btnTPMinusMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPMinusMinus.Text("--");
            btnTPMinusMinus.Color(clrYellow);
            btnTPMinusMinus.ColorBackground(clrDarkRed);
        }

        //
        // SL Label ...
        int lblSLX1 = btnCloseAllX1;
        int lblSLX2 = lblSLX1 + defaultWidth;
        int lblSLY1 = btnCloseAllY2 + defaultGap;
        int lblSLY2 = lblTPY1 + defaultHeight;
        string lblSLName = objSuffix + "LBL_SL";
        result = lblSL.Create(
            chartId,
            lblSLName,
            subWindow,
            lblSLX1,
            lblSLY1,
            lblSLX2,
            lblSLY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            lblSL.Disable();
            lblSL.Text("SL");
            lblSL.Color(clrWhite);
            lblSL.ColorBackground(clrDarkRed);
        }

        //
        // SL Plus ...
        int btnSLPlusX1 = lblSLX1;
        int btnSLPlusX2 = btnSLPlusX1 + minusPlusWidth;
        int btnSLPlusY1 = lblSLY2 + defaultGap;
        int btnSLPlusY2 = btnSLPlusY1 + defaultHeight;
        string btnSLPlusName = objSuffix + "BTN_SL_PLUS";
        result = btnSLPlus.Create(
            chartId,
            btnSLPlusName,
            subWindow,
            btnSLPlusX1,
            btnSLPlusY1,
            btnSLPlusX2,
            btnSLPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLPlus.Text("+");
            btnSLPlus.Color(clrYellow);
            btnSLPlus.ColorBackground(clrGreen);
        }

        //
        // SL Plus Plus ...
        int btnSLPlusPlusX1 = btnSLPlusX2 + defaultGap;
        int btnSLPlusPlusX2 = btnSLPlusPlusX1 + minusPlusWidth;
        int btnSLPlusPlusY1 = lblSLY2 + defaultGap;
        int btnSLPlusPlusY2 = btnSLPlusPlusY1 + defaultHeight;
        string btnSLPlusPlusName = objSuffix + "BTN_SL_PLUSPLUS";
        result = btnSLPlusPlus.Create(
            chartId,
            btnSLPlusPlusName,
            subWindow,
            btnSLPlusPlusX1,
            btnSLPlusPlusY1,
            btnSLPlusPlusX2,
            btnSLPlusPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLPlusPlus.Text("++");
            btnSLPlusPlus.Color(clrYellow);
            btnSLPlusPlus.ColorBackground(clrGreen);
        }

        //
        // SL Minus ...
        int btnSLMinusX1 = lblSLX2 - minusPlusWidth;
        int btnSLMinusX2 = btnSLMinusX1 + minusPlusWidth;
        int btnSLMinusY1 = lblSLY2 + defaultGap;
        int btnSLMinusY2 = btnSLMinusY1 + defaultHeight;
        string btnSLMinusName = objSuffix + "BTN_SL_MINUS";
        result = btnSLMinus.Create(
            chartId,
            btnSLMinusName,
            subWindow,
            btnSLMinusX1,
            btnSLMinusY1,
            btnSLMinusX2,
            btnSLMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLMinus.Text("-");
            btnSLMinus.Color(clrYellow);
            btnSLMinus.ColorBackground(clrDarkRed);
        }

        //
        // SL Minus Minus ...
        int btnSLMinusMinusX1 = btnSLMinusX1 - minusPlusWidth - defaultGap;
        int btnSLMinusMinusX2 = btnSLMinusMinusX1 + minusPlusWidth;
        int btnSLMinusMinusY1 = lblSLY2 + defaultGap;
        int btnSLMinusMinusY2 = btnSLMinusY1 + defaultHeight;
        string btnSLMinusMinusName = objSuffix + "BTN_SL_MINUSMINUS";
        result = btnSLMinusMinus.Create(
            chartId,
            btnSLMinusMinusName,
            subWindow,
            btnSLMinusMinusX1,
            btnSLMinusMinusY1,
            btnSLMinusMinusX2,
            btnSLMinusMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLMinusMinus.Text("--");
            btnSLMinusMinus.Color(clrYellow);
            btnSLMinusMinus.ColorBackground(clrDarkRed);
        }

        //
        UpdateGUIState();

        //
        return result;
    }

    /**
     * Update GUI based on Each Ticks ...
     */
    void UpdateGUI()
    {
        //
        UpdateGUIState();

        //
        // Buy Button Pressed ...
        if (btnBuy.Pressed())
        {
            //
            HandleBuyTrade();
            btnBuy.Pressed(false);
        }

        //
        // Sell Button Pressed ...
        if (btnSell.Pressed())
        {
            //
            HandleSellTrade();
            btnSell.Pressed(false);
        }

        //
        // Close Button Pressed ...
        if (btnClose.Pressed())
        {
            //
            if (btnClose.IsEnabled())
            {
                //
                if (selectedPositionID > 0)
                {
                    HandleCloseTrade(selectedPositionID);
                }
            }

            //
            btnClose.Pressed(false);
        }

        //
        // Close All Button Pressed ...
        if (btnCloseAll.Pressed())
        {
            //
            if (btnCloseAll.IsEnabled())
            {
                HandleCloseTrade(0);
            }

            //
            btnCloseAll.Pressed(false);
        }

        //
        // Check Show Hide Selected Position Info ...
        if (btnSelectedPosInfo != NULL)
        {
            //
            if (btnSelectedPosInfo.Pressed())
            {
                //
                // Check Enable ...
                if (btnSelectedPosInfo.IsEnabled())
                {
                    //
                    // Handle Toggle Show/Hide lblSelectedPositionInfo ...
                    CEdit *iLbl = NULL;
                    bool isVisible = showSelectedPositionInfo;
                    int count = selectedPosInfoArr.Total();
                    if (IsValidSize(count))
                    {
                        //
                        // Loop Through Labels ...
                        for (int i = 0; i < count; i++)
                        {
                            //
                            iLbl = (CEdit *)selectedPosInfoArr.At(i);
                            isVisible = iLbl.IsVisible();
                            if (isVisible)
                            {
                                iLbl.Hide();
                            }
                            else
                            {
                                iLbl.Show();
                            }
                        }

                        //
                        // Update Button Text ...
                        isVisible = showSelectedPositionInfo;
                        string btnText = isVisible
                                             ? "Hide Info"
                                             : "Show Info";
                        btnSelectedPosInfo.Text(btnText);
                        showSelectedPositionInfo = !showSelectedPositionInfo;
                    }

                    //
                    ZeroMemory(iLbl);
                }

                //
                btnSelectedPosInfo.Pressed(false);
            }
        }

        //
        // TP Managing ...

        //
        // TP Plus ...
        if (btnTPPlus.Pressed())
        {
            //
            if (btnTPPlus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BULLISH);
            }

            //
            btnTPPlus.Pressed(false);
        }

        //
        // TP Plus Plus ...
        if (btnTPPlusPlus.Pressed())
        {
            //
            if (btnTPPlusPlus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BULLISH, 2);
            }

            //
            btnTPPlusPlus.Pressed(false);
        }

        //
        // TP Minus ...
        if (btnTPMinus.Pressed())
        {
            //
            if (btnTPMinus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BEARISH);
            }

            //
            btnTPMinus.Pressed(false);
        }

        //
        // TP Minus Minus ...
        if (btnTPMinusMinus.Pressed())
        {
            //
            if (btnTPMinusMinus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BEARISH, 2);
            }

            //
            btnTPMinusMinus.Pressed(false);
        }

        //
        // SL Managing ...

        //
        // SL Plus ...
        if (btnSLPlus.Pressed())
        {
            //
            if (btnSLPlus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BULLISH);
            }

            //
            btnSLPlus.Pressed(false);
        }

        //
        // SL Plus Plus ...
        if (btnSLPlusPlus.Pressed())
        {
            //
            if (btnSLPlusPlus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BULLISH, 2);
            }

            //
            btnSLPlusPlus.Pressed(false);
        }

        //
        // SL Minus ...
        if (btnSLMinus.Pressed())
        {
            //
            if (btnSLMinus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BEARISH);
            }

            //
            btnSLMinus.Pressed(false);
        }

        //
        // SL Minus Minus ...
        if (btnSLMinusMinus.Pressed())
        {
            //
            if (btnSLMinusMinus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BEARISH, 2);
            }

            //
            btnSLMinusMinus.Pressed(false);
        }

        //
        // Check Positions Buttons ...
        int posCount = posArray.Total();
        bool has = IsValidSize(posCount);
        if (has)
        {
            //
            for (int i = 0; i < posCount; i++)
            {
                //
                CButton *iBtn = (CButton *)posArray.At(i);

                //
                if (iBtn.Pressed())
                {
                    //
                    if (iBtn.IsEnabled())
                    {
                        //
                        ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());
                        if (posID > 0 && selectedPositionID != posID)
                        {
                            selectedPositionID = posID;
                            UpdateGUIState();
                        }
                    }

                    //
                    iBtn.Pressed(false);
                }

                //
                ZeroMemory(iBtn);
            }
        }
    }

    /**
     * Destroy all Initialized GUi Requirements ...
     */
    void DestroyGUI()
    {
        //
        delete eaChartHelper;
        ZeroMemory(eaChartHelper);

        //
        delete btnSelectedPosInfo;
        ZeroMemory(btnSelectedPosInfo);
    }

    /**
     * Update GUI Element Styles ...
     */
    void UpdateGUIState()
    {
        //
        // Update States Of Position Select Buttons ...
        int count = posArray.Total();
        bool has = IsValidSize(count) && selectedPositionID > 0;
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                //
                CButton *iBtn = (CButton *)posArray.At(i);

                //
                ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());

                //
                // Select Position ...
                XPosition iPos;
                bool hasPosition = eaTrader.GetPosition(
                    posID,
                    iPos //
                );
                string iTooltip =
                    !hasPosition
                        ? ""
                    : IsLong(iPos.type)
                        ? "Long"
                        : "Short";

                //
                if (posID == selectedPositionID)
                {
                    //
                    iBtn.Disable();
                    iBtn.ColorBackground(clrDarkGray);
                }
                else
                {
                    //
                    iBtn.Enable();

                    //
                    color iBtnBG = IsLong(iPos.type)
                                       ? clrGreen
                                       : clrDarkRed;

                    //
                    iBtn.ColorBackground(iBtnBG);
                }

                //
                ZeroMemory(iBtn);
            }
        }

        //
        // Update State of Close All ...
        if (has && count > 1)
        {
            //
            btnCloseAll.Enable();
            btnCloseAll.ColorBackground(clrDarkRed);
        }
        else
        {
            //
            btnCloseAll.Disable();
            btnCloseAll.ColorBackground(clrDarkGray);
        }

        //
        // Update Selected Position Info Button State ...
        if (has)
        {
            //
            // Reading Global Chart Info ...
            int subWindow = 0;
            int chartWidth = eaChartHelper.Width();
            ulong chartId = eaChartHelper.ChartId();

            //
            // Read Selected Position ...
            XPosition selectedPosition;
            bool hasPosition = eaTrader.GetPosition(
                selectedPositionID,
                selectedPosition //
            );

            //
            // Read All Positions ...
            double profits = 0;
            XPosition positions[];
            string positionsInfo = "";
            int positionsCount = eaTrader.GetPositions(positions);
            has = IsValidSize(positionsCount);
            if (has)
            {
                //
                for (int i = 0; i < positionsCount; i++)
                {
                    profits += positions[i].profit;
                }

                //
                if (positionsCount > 1)
                {
                    positionsInfo = "Positions Count: " + ToXString(positionsCount) + ", Profits: " + ToXString(profits);
                }
            }

            //
            if (hasPosition)
            {
                //
                // Preparing Position Info ...
                string posTicket = "Ticket: " + ToXString(selectedPosition.ticket);
                string posType = "Type: " + ToXString(selectedPosition.type);
                string posSymbolPeriod =
                    "Symbol: " + selectedPosition.symbol + ", " +
                    "Period: " + ToXString(selectedPosition.period);
                string posProfit = "Profit: " + ToXString(selectedPosition.profit);
                string posRiskToRewardRatio = "RiskToReward: " + ToXString(selectedPosition.GetRiskRewardRatio());
                string accountInfo =
                    "Balance: " + ToXString(eaTrader.mAccount.GetBalance()) + ", " +
                    "Equity: " + ToXString(eaTrader.mAccount.GetEquity());

                //
                string posInfos[];

                //
                // Ticket ...
                Add(
                    posTicket,
                    posInfos //
                );

                //
                // Type ...
                Add(
                    posType,
                    posInfos //
                );

                //
                // Symbol Period ...
                Add(
                    posSymbolPeriod,
                    posInfos //
                );

                //
                // Profit ...
                Add(
                    posProfit,
                    posInfos //
                );

                //
                // Risk to Reward Ratio ...
                Add(
                    posRiskToRewardRatio,
                    posInfos //
                );

                //
                // Add Positions Info if Exists ...
                if (IsSpecifiedValid(positionsInfo))
                {
                    //
                    Add(
                        positionsInfo,
                        posInfos //
                    );

                    //
                    // Clear Info Array ...
                    selectedPosInfoArr.Clear();
                }

                //
                // Adding Account Info ...
                Add(
                    accountInfo,
                    posInfos //
                );

                //
                // Create Button If Not Exists ...
                if (btnSelectedPosInfo == NULL)
                {
                    //
                    btnSelectedPosInfo = new CButton();

                    //
                    string name = objSuffix + "BTN_TOOGLE_SPOS_INFO";

                    //
                    CRect iRect = btnTPPlus.Rect();

                    //
                    int x1 = iRect.left;
                    int x2 = x1 + 200 + 5;

                    //
                    int y1 = iRect.bottom + 5;
                    int y2 = y1 + iRect.Height();

                    //
                    bool isCreated = btnSelectedPosInfo.Create(
                        chartId,
                        name,
                        subWindow,
                        x1,
                        y1,
                        x2,
                        y2 //
                    );
                    if (!isCreated)
                    {
                        //
                        delete btnSelectedPosInfo;
                        ZeroMemory(btnSelectedPosInfo);
                    }
                    else
                    {
                        //
                        // Apply Styles ...
                        btnSelectedPosInfo.Color(clrYellow);
                        btnSelectedPosInfo.ColorBackground(clrDarkBlue);
                    }
                }

                //
                // Check Btn Exists ...
                has = btnSelectedPosInfo != NULL;
                if (has)
                {
                    //
                    int posInfoCount = ArraySize(posInfos);
                    int lblInfoCount = selectedPosInfoArr.Total();
                    has = IsValidSize(lblInfoCount) && lblInfoCount == posInfoCount;

                    //
                    // Create info Labels ...
                    if (!has)
                    {
                        //
                        selectedPosInfoArr.Clear();
                        has = IsValidSize(posInfoCount);
                        if (has)
                        {
                            //
                            // Creat Info Control ...

                            //
                            string name = objSuffix + "LBL_SPOS_INFO";

                            //
                            int lblHeight = 25;
                            int lblWidth = 450;

                            //
                            int startX = chartWidth - lblWidth - 5;
                            int sizeX = startX + lblWidth;

                            //
                            int startY = 5;
                            int sizeY = startY + lblHeight;

                            //
                            // Loop through Position Info's Array to Create Label Controls ...
                            for (int i = 0; i < posInfoCount; i++)
                            {
                                //
                                string iText = posInfos[i];
                                string iTextMD5 = ToMD5(iText);

                                //
                                string iName = name + "_" + iTextMD5;

                                //
                                int iX1 = startX;
                                int iX2 = sizeX;

                                //
                                int iY1 = startY;
                                int iY2 = startY + lblHeight;

                                //
                                CEdit *iLbl = new CEdit();
                                bool isCreated = iLbl.Create(
                                    chartId,
                                    iName,
                                    subWindow,
                                    iX1,
                                    iY1,
                                    iX2,
                                    iY2 //
                                );
                                if (isCreated)
                                {
                                    //
                                    iLbl.Text(iText);
                                    iLbl.Color(clrYellow);
                                    iLbl.ColorBorder(clrDarkSlateGray);
                                    iLbl.ColorBackground(clrDarkSlateGray);

                                    //
                                    if (showSelectedPositionInfo)
                                    {
                                        iLbl.Show();
                                    }
                                    else
                                    {
                                        iLbl.Hide();
                                    }

                                    //
                                    selectedPosInfoArr.Add(iLbl);

                                    //
                                    startY = iY2;
                                }
                            }
                        }
                    }

                    //
                    // Update Info Labels ...
                    lblInfoCount = selectedPosInfoArr.Total();
                    has = IsValidSize(lblInfoCount);
                    if (has)
                    {
                        //
                        CEdit *iLbl = NULL;

                        //
                        // Loop Through Infos ...
                        for (int i = 0; i < lblInfoCount; i++)
                        {
                            //
                            iLbl = (CEdit *)selectedPosInfoArr.At(i);

                            //
                            iLbl.Text(posInfos[i]);
                        }

                        //
                        bool isVisible = showSelectedPositionInfo;
                        string btnText = isVisible
                                             ? "Hide Info"
                                             : "Show Info";
                        btnSelectedPosInfo.Text(btnText);

                        //
                        ZeroMemory(iLbl);
                    }
                }

                //
                ZeroMemory(posInfos);
            }

            //
            SpecifiedClean(positions);
        }
        else
        {
            //
            // Toggle Selected Position Btn ...
            if (btnSelectedPosInfo != NULL)
            {
                //
                btnSelectedPosInfo.Destroy();

                //
                delete btnSelectedPosInfo;
                ZeroMemory(btnSelectedPosInfo);
            }

            //
            // Selected Position Lbls ...
            selectedPosInfoArr.Clear();
        }

        //
        // Update State of BtnClose and BtnClose All ...
        if (selectedPositionID == 0)
        {
            //
            btnClose.Disable();
            btnClose.ColorBackground(clrDarkGray);

            //
            lblTP.Disable();
            lblTP.ColorBackground(clrDarkGray);

            //
            btnTPPlus.Disable();
            btnTPPlus.ColorBackground(clrDarkGray);

            //
            btnTPPlusPlus.Disable();
            btnTPPlusPlus.ColorBackground(clrDarkGray);

            //
            btnTPMinus.Disable();
            btnTPMinus.ColorBackground(clrDarkGray);

            //
            btnTPMinusMinus.Disable();
            btnTPMinusMinus.ColorBackground(clrDarkGray);

            //
            lblSL.Disable();
            lblSL.ColorBackground(clrDarkGray);

            //
            btnSLPlus.Disable();
            btnSLPlus.ColorBackground(clrDarkGray);

            //
            btnSLPlusPlus.Disable();
            btnSLPlusPlus.ColorBackground(clrDarkGray);

            //
            btnSLMinus.Disable();
            btnSLMinus.ColorBackground(clrDarkGray);

            //
            btnSLMinusMinus.Disable();
            btnSLMinusMinus.ColorBackground(clrDarkGray);
        }
        else
        {
            //
            btnClose.Enable();
            btnClose.ColorBackground(clrDarkOrange);

            //
            lblTP.Enable();
            lblTP.ColorBackground(clrGreen);

            //
            btnTPPlus.Enable();
            btnTPPlus.ColorBackground(clrGreen);

            //
            btnTPPlusPlus.Enable();
            btnTPPlusPlus.ColorBackground(clrGreen);

            //
            btnTPMinus.Enable();
            btnTPMinus.ColorBackground(clrDarkRed);

            //
            btnTPMinusMinus.Enable();
            btnTPMinusMinus.ColorBackground(clrDarkRed);

            //
            lblSL.Enable();
            lblSL.ColorBackground(clrDarkRed);

            //
            btnSLPlus.Enable();
            btnSLPlus.ColorBackground(clrGreen);

            //
            btnSLPlusPlus.Enable();
            btnSLPlusPlus.ColorBackground(clrGreen);

            //
            btnSLMinus.Enable();
            btnSLMinus.ColorBackground(clrDarkRed);

            //
            btnSLMinusMinus.Enable();
            btnSLMinusMinus.ColorBackground(clrDarkRed);
        }
    }

    /**
     * Update Position's Selector Buttons ...
     */
    void UpdatePositionButtons()
    {
        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = eaTrader.GetPositions(
            positions //
        );
        bool has = IsValidSize(count);
        if (!has)
        {
            //
            posArray.Clear();
            selectedPositionID = 0;
            UpdateGUIState();
            return;
        }

        //
        posArray.Clear();

        //
        // Selected Position ID ...
        bool isSelectdPositionIDExists = false;
        if (has && selectedPositionID > 0)
        {
            //
            for (int i = 0; i < count; i++)
            {
                //
                if (!isSelectdPositionIDExists &&
                    positions[i].ticket == selectedPositionID)
                {
                    //
                    isSelectdPositionIDExists = true;
                    break;
                }
            }
        }
        if (!isSelectdPositionIDExists ||
            selectedPositionID == 0)
        {
            //
            XPosition youngest;
            int youngestIDX = GetXYoungest(youngest, positions);
            bool hasYoungest = IsValidIndex(youngestIDX) &&
                               youngest.IsValid();
            if (hasYoungest)
            {
                selectedPositionID = youngest.ticket;
            }

            //
            youngest.Clean();
        }

        //
        int subWindow = 0;
        ulong chartId = eaChartHelper.ChartId();

        //
        int btnWidth = 25;
        int btnHeight = 25;

        //
        CRect iRect = lblPositionId.Rect();

        //
        int lastX = iRect.left + iRect.Width() + 5;
        int lastSizeX = lastX + btnWidth;

        //
        int lastY = iRect.top;
        int lastSizeY = lastY + iRect.Height();

        //
        for (int i = 0; i < count; i++)
        {
            //
            XPosition iPos = positions[i];

            //
            string posTypeStr = IsLong(iPos.type)
                                    ? "LONG"
                                    : "SHORT";

            //
            int iPosBtnX = lastX;
            int iPosBtnSizeX = iPosBtnX + btnWidth;
            string iPosBtnName = objSuffix + "_" + posTypeStr + "_POS_SELECT_BTN_" + ToXString(iPos.ticket);

            //
            CButton *iPosBtn = new CButton();
            has = iPosBtn.Create(
                chartId,
                iPosBtnName,
                subWindow,
                iPosBtnX,
                lastY,
                iPosBtnSizeX,
                lastSizeY //
            );
            if (has)
            {
                //
                // Apply Styles ...
                string iPosBtnText = ToXString(iPos.ticket);

                //
                iPosBtn.Text(iPosBtnText);
                iPosBtn.Color(clrWhite);

                //
                color iPosBtnBG = IsLong(iPos.type)
                                      ? clrGreen
                                      : clrDarkRed;
                iPosBtn.ColorBackground(iPosBtnBG);

                //
                lastX = iPosBtnSizeX + 5;

                //
                if (selectedPositionID == iPos.ticket)
                {
                    iPosBtn.Disable();
                }

                //
                posArray.Add(iPosBtn);
            }

            //
            iPos.Clean();
        }

        //
        ZeroMemory(positions);
        UpdateGUIState();
    }

    //
    // GUI Event Handlers ...

    //
    void HandleBuyTrade()
    {
        HandleTrade(X_DIRECTION_BULLISH);
    }

    //
    void HandleSellTrade()
    {
        HandleTrade(X_DIRECTION_BEARISH);
    }

    //
    void HandleTrade(ENUM_X_DIRECTION dir)
    {
        //
        if (!HasDirection(dir))
        {
            return;
        }

        //
        NormalizeProps();

        //
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double r2r = riskToRewardRatio;
        double _volume = volume;
        double _riskInPoints = riskInPoint;
        double point = GetPoints(symbol);
        double entry = GetEntry(symbol, dir);

        //
        double risk = _riskInPoints * point;
        double reward = risk * r2r;

        //
        double sl = isBullish
                        ? entry - risk
                        : entry + risk;
        double tp = isBullish
                        ? entry + reward
                        : entry - reward;

        //
        string comment = eaLogSuffix + "_POS_" + ToXString(dir);

        //
        if (isBullish)
        {
            //
            eaTrader.Buy(
                symbol,
                period,
                _volume,
                entry,
                sl,
                tp,
                comment //
            );
        }
        else
        {
            //
            eaTrader.Sell(
                symbol,
                period,
                _volume,
                entry,
                sl,
                tp,
                comment //
            );
        }
    }

    //
    void HandleCloseTrade(ulong positionID)
    {
        //
        if (positionID == 0)
        {
            //
            XPosition positions[];
            int count = eaTrader.GetPositions(
                positions //
            );
            bool has = IsValidSize(count);
            if (has)
            {
                //
                int closedCount = eaTrader.Close(positions);
                has = IsValidSize(closedCount);
            }
        }
        else
        {
            //
            XPosition position;
            bool has = eaTrader.GetPosition(
                positionID,
                position //
            );
            if (has)
            {
                //
                has = eaTrader.Close(positionID);
            }
        }
    }

    //
    void HandleTPChange(
        ENUM_X_DIRECTION dir,
        int multiplier = 1 //
    )
    {
        //
        // Validate ...
        if (selectedPositionID == 0 || !HasDirection(dir))
        {
            return;
        }

        //
        NormalizeProps();

        //
        // Normalize Multiplier ...
        multiplier = NormalizeInt(multiplier, 1, 2);

        //
        // Select Position ...
        XPosition position;
        bool has = eaTrader.GetPosition(
            selectedPositionID,
            position //
        );
        if (!has)
        {
            //
            position.Clean();
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double point = GetPoints(position.symbol);
        double stepValue = multiplier * (point * allowedTPSLStep);

        //
        bool mustIncrease =
            isBullish
                ? isLong
                : !isLong;

        //
        double tp = position.tp;
        tp =
            mustIncrease
                ? tp + stepValue
                : tp - stepValue;
        bool isModified = eaTrader.Modify(
            position.ticket,
            position.sl,
            tp,
            objSuffix + "Change TP" //
        );
        if (isModified)
        {
            UpdateGUIState();
        }

        //
        position.Clean();
    }

    //
    void HandleSLChange(
        ENUM_X_DIRECTION dir,
        int multiplier = 1 //
    )
    {
        //
        // Validate ...
        if (selectedPositionID == 0 || !HasDirection(dir))
        {
            return;
        }

        //
        NormalizeProps();

        //
        // Normalize Multiplier ...
        multiplier = NormalizeInt(multiplier, 1, 2);

        //
        // Select Position ...
        XPosition position;
        bool has = eaTrader.GetPosition(
            selectedPositionID,
            position //
        );
        if (!has)
        {
            //
            position.Clean();
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double point = GetPoints(position.symbol);
        double stepValue = multiplier * (point * allowedTPSLStep);

        //
        bool mustIncrease =
            isBullish
                ? !isLong
                : isLong;

        //
        double risk = position.GetRiskInPoint();
        if (risk <= minAllowedRiskInPoint)
        {
            //
            position.Clean();
            return;
        }

        //
        double sl = position.sl;
        sl =
            mustIncrease
                ? sl + stepValue
                : sl - stepValue;
        bool isModified = eaTrader.Modify(
            position.ticket,
            sl,
            position.tp,
            objSuffix + "Change SL" //
        );
        if (isModified)
        {
            UpdateGUIState();
        }

        //
        position.Clean();
    }

    //
    // Custom Tools ...

    //
    void NormalizeProps()
    {
        //
        if (volume < 0.01)
        {
            volume = 0.01;
        }

        //
        if (riskInPoint < 0)
        {
            riskInPoint = 50;
        }

        //
        if (allowedTPSLStep < 5)
        {
            allowedTPSLStep = 5;
        }

        //
        if (riskToRewardRatio < 0.5)
        {
            riskToRewardRatio = 2;
        }

        //
        if (minAllowedRiskInPoint < 10)
        {
            minAllowedRiskInPoint = 10;
        }
    }

    //
    ulong ExtractPositionIDFromButtonName(string name)
    {
        //
        ulong result = 0;

        //
        string nameParts[];
        int namePartsCount = SplitContent(nameParts, name, "_");
        bool has = IsValidSize(namePartsCount);
        if (has)
        {
            //
            string lastPart = nameParts[namePartsCount - 1];
            StringReplace(lastPart, "_", "");
            result = (long)lastPart;
        }

        //
        ZeroMemory(nameParts);

        //
        return result;
    }
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-bar.analyser.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XPOI
// Description: Bar Analysing ...
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"

//
// Definitions ...

//
// Extensions ...
void FillLiquidity(
    XOHCL &lBar,
    ENUM_X_DIRECTION lDir,
    XBoxZone &lq,
    datetime to //
)
{
    //
    // Normalize Args ...
    lq.Clean();
    to = NormalizeTime(to);

    //
    // Validate Args ...
    bool has = lBar.IsValid() &&
               HasDirection(lDir);
    if (!has)
    {
        return;
    }

    //
    lq.to = to;
    lq.dir = lDir;
    lq.type = "XLQ";
    lq.from = lBar.time;
    lq.symbol = lBar.symbol;
    lq.period = lBar.period;
    lq.upper = IsBullish(lDir)
                   ? lBar.GetDown()
                   : lBar.high;
    lq.lower = IsBullish(lDir)
                   ? lBar.low
                   : lBar.GetUp();
}

//
// Implementations ...
class XCBarAnalyser : public XCBase
{
    //
  public:
    //

    //
    // Constructor(s) ...
    XCBarAnalyser()
    {
    }

    //
    // Deconstructor ...
    ~XCBarAnalyser()
    {
    }

    //
    // Getter(s) / Setter(s) ...

    //
    // Providers ...

    /**
     * Calculate Loopback Data for Specific Bar ...
     *
     * @param  index: Integer, Bar Current Index ...
     * @param  start: Integer, Start of Loopback ...
     * @param  end: Integer, End of Loopback ...
     * @param  count: Integer, Number of Calculating Bars ...
     * @param  bar: XOHCL instance, Specified Bar ...
     * @param  loopback: Integer, Loopback Length ...
     * @return ( bool )
     */
    bool CalculateLoopbackData(
        int &index,
        int &start,
        int &end,
        int &count,
        XOHCL &bar,
        int loopback = 1 //
    )
    {
        //
        bool result = false;

        //
        if (loopback < 1)
        {
            loopback = 1;
        }

        //
        index = -1;
        start = -1;
        end = -1;
        count = -1;

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        index = bar.Index();
        start = index + 1;
        end = start + loopback;
        count = end - start;
        result = count >= 1;
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    /**
     * Retrieve Loopback HH & LL Data ...
     *
     * @param  hhIDX: Integer, HH Index ...
     * @param  hh: Double, HH Value ...
     * @param  llIDX: Integer, LL Index ...
     * @param  ll: Double, LL Value ...
     * @param  bar: XOHCL instance ...
     * @param  loopback: Integer, Loopback Length ...
     *
     * @return ( bool )
     */
    bool CalculateLoopbackHLData(
        int &hhIDX,
        double &hh,
        int &llIDX,
        double &ll,
        XOHCL &bar,
        int loopback //
    )
    {
        //
        bool result = false;

        //
        if (loopback < 1)
        {
            loopback = 1;
        }

        //
        int index = -1;
        int start = -1;
        int end = -1;
        int count = -1;
        result = CalculateLoopbackData(
            index,
            start,
            end,
            count,
            bar,
            loopback //
        );
        if (!result)
        {
            return result;
        }

        //
        hhIDX = iHighest(
            bar.symbol,
            bar.period,
            MODE_HIGH,
            count,
            start //
        );
        result = IsValidIndex(hhIDX);
        if (!result)
        {
            return result;
        }

        //
        llIDX = iLowest(
            bar.symbol,
            bar.period,
            MODE_LOW,
            count,
            start //
        );
        result = IsValidIndex(llIDX);
        if (!result)
        {
            return result;
        }

        //
        hh = iHigh(
            bar.symbol,
            bar.period,
            hhIDX //
        );
        ll = iLow(
            bar.symbol,
            bar.period,
            llIDX //
        );

        //
        return result;
    }

    /**
     * Fill Prices ...
     *
     * @param  prices: double collection
     * @param  bar: XOHCL instance reference, Provides Start ...
     * @param  loopback: int, Specified How Many Bars Process ...
     * @param  type: ENUM_X_PRICE member, Specified Price Type ...
     *
     * @return ( bool )
     */
    int FillPrice(
        double &prices[],
        XOHCL &bar,
        int loopback = 14,
        ENUM_X_PRICE type = X_PRICE_CLOSE //
    )
    {
        //
        int result = 0;

        //
        Clean(prices);

        //
        loopback = NormalizeInt(loopback, 1);

        //
        bool has = IsValid(type) &&
                   bar.IsValid();
        if (!has)
        {
            return result;
        }

        //
        string symbol = bar.symbol;
        ENUM_TIMEFRAMES period = bar.period;

        //
        // Calculate Loopback Data ...
        int index = -1;
        int start = -1;
        int end = -1;
        int count = -1;
        has = CalculateLoopbackData(
            index,
            start,
            end,
            count,
            bar,
            loopback //
        );
        if (!has)
        {
            return result;
        }

        //
        for (int i = start; i < end; i++)
        {
            //
            XOHCL iBar;
            bool isInited = iBar.Init(
                symbol,
                period,
                i //
            );
            if (!isInited)
            {
                //
                iBar.Clean();
                continue;
            }

            //
            double iPrice = iBar.GetPrice(type);

            //
            isInited = NotEmptyZero(iPrice);
            if (isInited)
            {
                //
                Add(
                    iPrice,
                    prices //
                );
            }

            //
            iBar.Clean();
        }

        //
        result = ArraySize(prices);
        has = result == count;
        if (!has)
        {
            //
            Clean(prices);
        }

        //
        ArrayReverse(prices);

        //
        return result;
    }

    /**
     * Calculate Price Range ...
     *
     * @param  ranges: double Collection, Calculated Ranges ...
     * @param  bar: XOHCL instance Reference, Start Bar ...
     * @param  forceBody: bool, Force to Calculate Range using Body ...
     * @param  forceDiff: bool, Force to Calculate Range using Onl Price Diff ...
     * @param  loopback: int, loopback period for Range Calculations ...
     *
     * @return ( int )
     */
    int GetPriceRange(
        double &ranges[],
        XOHCL &bar,
        bool forceBody = false,
        bool forceDiff = false,
        int loopback = 14 //
    )
    {
        //
        int result = 0;

        //
        Clean(ranges);

        //
        loopback = NormalizeInt(loopback, 1);

        //
        if (!bar.IsValid())
        {
            return result;
        }

        //
        bool has = false;

        //
        // Select Uppers Prices ...
        double uppers[];
        int uppersCount = 0;
        if (forceBody)
        {
            //
            has = FillPrice(
                uppers,
                bar,
                loopback,
                X_PRICE_UP //
            );
        }
        else
        {
            //
            has = FillPrice(
                uppers,
                bar,
                loopback,
                X_PRICE_HIGH //
            );
        }
        if (has)
        {
            uppersCount = ArraySize(uppers);
        }

        //
        // Select Lower Prices ...
        double lowers[];
        int lowersCount = 0;
        if (forceBody)
        {
            //
            has = FillPrice(
                lowers,
                bar,
                loopback,
                X_PRICE_DOWN //
            );
        }
        else
        {
            //
            has = FillPrice(
                lowers,
                bar,
                loopback,
                X_PRICE_LOW //
            );
        }
        if (has)
        {
            lowersCount = ArraySize(lowers);
        }

        //
        // Validate Filled Prices ...
        has =
            IsValidSize(uppersCount) &&
            IsValidSize(lowersCount) &&
            uppersCount == lowersCount;
        if (!has)
        {
            //
            Clean(uppers);
            Clean(lowers);

            //
            return result;
        }

        //
        for (int i = 0; i < uppersCount; i++)
        {
            //
            double iDiff = uppers[i] - lowers[i];
            double iValue =
                forceDiff
                    ? iDiff
                    : lowers[i] + iDiff;

            //
            Add(
                iValue,
                ranges //
            );
        }

        //
        result = ArraySize(ranges);
        ArrayReverse(ranges);

        //
        // Cleanup Resources ...

        //
        Clean(uppers);
        Clean(lowers);

        //
        return result;
    }

    /**
     * Retrieve Price Velocities ...
     *
     * @param  velocities: double Collection, Price Velocities ...
     * @param  bar: XOHCL instance Reference, Start Bar ...
     * @param  type: ENUM_X_PRICE member ...
     * @param  loopback: int, loopback period for Calculations ...
     *
     * @return ( int )
     */
    int GetPriceVelocity(
        double &velocities[],
        XOHCL &bar,
        ENUM_X_PRICE type = X_PRICE_CLOSE,
        int loopback = 14 //
    )
    {
        //
        int result = 0;

        //
        Clean(velocities);
        loopback = NormalizeInt(loopback, 1);

        //
        result = IsValid(type) &&
                 bar.IsValid();

        //
        int to = bar.Index();
        int from = to + loopback;
        for (int i = to; i < from; i++)
        {
            //
            double iPrice = GetAppliedPrice(
                bar.symbol,
                bar.period,
                i,
                type //
            );

            //
            double pPrice = GetAppliedPrice(
                bar.symbol,
                bar.period,
                i + 1,
                type //
            );

            //
            double iVelocity = MathAbs(iPrice - pPrice);
            Add(
                iVelocity,
                velocities //
            );
        }

        //
        result = ArraySize(velocities);

        //
        return result;
    }

    /**
     * Calculate Price Change Percent ...
     *
     * @param  bar: XOHCL instance Reference, Start Bar ...
     * @param  type: ENUM_X_PRICE member, Specified Price Type ...
     * @param  loopback: int, loopback period for Range Calculated ...
     *
     * @return ( double )
     */
    double GetPriceChangeRate(
        XOHCL &bar,
        ENUM_X_PRICE type = X_PRICE_MEDIAN,
        int loopback = 14 //
    )
    {
        //
        double result = 0;

        //
        bool has = false;

        //
        loopback = NormalizeInt(loopback, 1);

        //
        has =
            bar.IsValid() &&
            IsValid(type);
        if (!has)
        {
            return result;
        }

        //
        XOHCL oldBar;
        has = oldBar.Init(
            bar.symbol,
            bar.period,
            bar.Index() + loopback //
        );
        if (!has)
        {
            //
            oldBar.Clean();

            //
            return result;
        }
        double newPrice = bar.GetPrice(type);
        double oldPrice = oldBar.GetPrice(type);

        //
        result = ((newPrice - oldPrice) / oldPrice) / 100;

        //
        oldBar.Clean();

        //
        return result;
    }

    /**
     * Detect Range Block ...
     */
    bool DetectRangeBlocks(
        XOHCL &bar,
        XBoxZone &upper,
        XBoxZone &lower,
        int loopback = 288,
        ENUM_SERIESMODE upperRangeUpperMode = MODE_HIGH,
        ENUM_SERIESMODE upperRangeLowerMode = MODE_LOW,
        ENUM_SERIESMODE lowerRangeUpperMode = MODE_HIGH,
        ENUM_SERIESMODE lowerRangeLowerMode = MODE_LOW //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        upper.Clean();
        lower.Clean();
        loopback = NormalizeInt(loopback, 10);

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL iBar;
        result = iBar.Init(
            bar.symbol,
            bar.period,
            bar.Index() + loopback //
        );
        if (!result)
        {
            //
            iBar.Clean();

            //
            return result;
        }

        //
        double hhUpper = iBar.FindHighest(loopback, upperRangeUpperMode);
        double hhLower = iBar.FindHighest(loopback, upperRangeLowerMode);

        //
        double llUpper = iBar.FindLowest(loopback, lowerRangeUpperMode);
        double llLower = iBar.FindLowest(loopback, lowerRangeLowerMode);

        //
        double bhhUpper = bar.FindHighest(iBar.Index() - bar.Index(), upperRangeUpperMode);
        double bhhLower = bar.FindHighest(iBar.Index() - bar.Index(), upperRangeLowerMode);

        //
        double bllUpper = bar.FindLowest(iBar.Index() - bar.Index(), lowerRangeUpperMode);
        double bllLower = bar.FindLowest(iBar.Index() - bar.Index(), lowerRangeLowerMode);

        //
        hhUpper = MathMax(hhUpper, bhhUpper);
        hhLower = MathMax(hhLower, bhhLower);

        //
        llUpper = MathMin(llUpper, bllUpper);
        llLower = MathMin(llLower, bllLower);

        //
        datetime from = GetBarTime(
            iBar.symbol,
            iBar.period,
            iBar.Index() + loopback //
        );

        //
        upper.from = from;
        upper.type = "XRNG";
        upper.to = bar.time;
        upper.upper = hhUpper;
        upper.lower = hhLower;
        upper.symbol = iBar.symbol;
        upper.period = iBar.period;
        upper.dir = X_DIRECTION_BEARISH;

        //
        lower.from = from;
        lower.type = "XRNG";
        lower.to = bar.time;
        lower.upper = llUpper;
        lower.lower = llLower;
        lower.symbol = iBar.symbol;
        lower.period = iBar.period;
        lower.dir = X_DIRECTION_BULLISH;

        //
        result = upper.IsValid() &&
                 lower.IsValid();
        if (!result)
        {
            //
            iBar.Clean();
            upper.Clean();
            lower.Clean();
        }

        //
        iBar.Clean();

        //
        return result;
    }

    /**
     * Calculate Specified Bars Range Volume ...
     *
     * @param  bar: XOHCL instance reference ...
     * @param  bullishVolume: double
     * @param  bearishVolume: double
     * @param  loopback: int
     *
     * @return ( bool )
     */
    bool CalculateRangeVolume(
        XOHCL &bar,
        double &bullishVolume,
        double &bearishVolume,
        int loopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        bullishVolume = 0;
        bearishVolume = 0;
        loopback = NormalizeInt(loopback, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL iBar;
        bool has = false;
        int start = bar.Index();
        int end = start + loopback;
        for (int i = start; i <= end; i++)
        {
            //
            iBar.Clean();
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );
            if (!has)
            {
                //
                iBar.Clean();

                //
                break;
            }

            //
            bool isBullish = iBar.IsBullish();
            bool isBearish = iBar.IsBearish();

            //
            if (isBullish)
            {
                bullishVolume += (double)iBar.volume;
            }
            else if (isBearish)
            {
                bearishVolume += (double)iBar.volume;
            }
            else
            {
                //
                bullishVolume += (double)iBar.volume;
                bearishVolume += (double)iBar.volume;
            }

            //
            iBar.Clean();
        }

        //
        result = iBar.IsValid();

        //
        iBar.Clean();

        //
        return result;
    }

    /**
     * Detect Same Bars ...
     *
     * @param  bar: XOHCL instance, Start Bar ...
     * @param  dir: ENUM_X_DIRECTION member, Holds Result Direction ...
     * @param  index: Integer, Holds Result Index ...
     * @param  sameBars: Iteger, Required Same Bar ...
     *
     * @return ( bool )
     */
    bool HasSameBars(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int &index,
        int sameBars = 3 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;

        //
        if (sameBars < 1)
        {
            sameBars = 1;
        }

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        index = bar.Index() + 1;
        bool canContinue = true;
        int sameBullishBarsFounded = 0;
        int sameBearishBarsFounded = 0;
        bool canContinueForBullish = true;
        bool canContinueForBearish = true;
        while (canContinue)
        {
            //
            XOHCL iBar;
            result = iBar.Init(
                bar.symbol,
                bar.period,
                index //
            );
            if (!result)
            {
                break;
            }

            //
            bool isBullish = iBar.IsBullish();
            bool isBearish = iBar.IsBearish();
            result = isBullish ||
                     isBearish;
            if (!result)
            {
                break;
            }

            //
            if (isBullish)
            {
                //
                sameBullishBarsFounded++;
                canContinueForBearish = false;
            }
            else if (isBearish)
            {
                //
                sameBearishBarsFounded++;
                canContinueForBullish = false;
            }
            else
            {
                //
                sameBullishBarsFounded = 0;
                sameBearishBarsFounded = 0;
                canContinueForBullish = false;
                canContinueForBearish = false;
            }

            //
            result = canContinueForBullish ||
                     canContinueForBearish;
            if (!result)
            {
                break;
            }

            //
            canContinueForBullish =
                canContinueForBullish &&
                sameBullishBarsFounded > 0;

            //
            canContinueForBearish =
                canContinueForBearish &&
                sameBearishBarsFounded > 0;

            //
            canContinue =
                canContinueForBullish ||
                canContinueForBearish;
            if (canContinue)
            {
                index++;
            }
        }

        //
        bool hasBullishSameBars =
            sameBullishBarsFounded >= sameBars;

        //
        bool hasBearishSameBars =
            sameBearishBarsFounded >= sameBars;

        //
        result = hasBullishSameBars ||
                 hasBearishSameBars;
        if (!result)
        {
            return result;
        }

        //
        index--;

        //
        dir =
            hasBullishSameBars
                ? X_DIRECTION_BULLISH
                : X_DIRECTION_BEARISH;

        //
        return result;
    }

    /**
     * Check Fibonacci Level 382 Pressure ...
     *
     * @param  bar: XOHCL instance ...
     * @param  dir: ENUM_X_DIRECTION member ...
     *
     * @return ( bool )
     */
    bool HasFiboPressure(
        XOHCL &bar,
        ENUM_X_DIRECTION forDir,
        ENUM_X_FIBO_LEVELS level //
    )
    {
        //
        bool result = false;

        //
        result = bar.IsValid() &&
                 HasDirection(forDir);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = IsBullish(forDir);
        bool isBearish = IsBearish(forDir);

        //
        double fiboLevelValue = GetFibonacciLevel(
            bar.high,
            bar.low,
            level,
            forDir //
        );

        //
        bool isBullishPressured =
            isBullish &&
            bar.GetDown() > fiboLevelValue;
        bool isBearishPressured =
            isBearish &&
            bar.GetUp() < fiboLevelValue;

        //
        result = isBullishPressured ||
                 isBearishPressured;

        //
        return result;
    }

    /**
     * Detect Big Price Change Happens or not ...
     *
     * @param  bar: XOHCL instance reference, Start Bar ...
     * @param  dir: ENUM_X_DIRECTION member, Event Direction ...
     * @param  rootIDX: int, Root of Momentum Index ...
     * @param  type: ENUM_X_PRICE member, Price Type ...
     * @param  forcBodyInRange: bool, Use Body in Range Detection ...
     * @param  rangeExtendMultiplier: double, a Multiplier for Checking Price Range ...
     *
     * @return ( bool )
     */
    bool IsSharp(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int &rootIDX,
        ENUM_X_PRICE type = X_PRICE_CLOSE,
        bool forcBodyInRange = true,
        double rangeExtendMultiplier = 2.0 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        rangeExtendMultiplier = NormalizeDouble(rangeExtendMultiplier, 1);

        //
        result =
            bar.IsValid() &&
            IsValid(type);
        if (!result)
        {
            return result;
        }

        //
        double minPriceChangePercent = 0;
        int loopback = GetPeriodSharpData(
            minPriceChangePercent,
            bar.period //
        );

        //
        // Retrieve Price Change Percent ...
        double priceChangePercent = GetPriceChangeRate(
            bar,
            type,
            loopback //
        );

        //
        // Velocities ...
        double velocities[];
        int velocitiesCount = GetPriceVelocity(
            velocities,
            bar,
            type,
            loopback //
        );
        double velocitiesAvg = GetAverage(velocities);
        double oldPrice = GetAppliedPrice(
            bar.symbol,
            bar.period,
            bar.Index() + loopback,
            type //
        );
        double newPrice = bar.GetPrice(type);
        double currVelocity = MathAbs(oldPrice - newPrice);

        //
        // Calculate HH and LL ...

        //
        double hh = 0;
        double ll = 0;
        int hhIDX = -1;
        int llIDX = -1;
        result = CalculateLoopbackHLData(
            hhIDX,
            hh,
            llIDX,
            ll,
            bar,
            loopback //
        );
        result =
            result &&
            ll > 0 &&
            hh > ll &&
            IsValidIndex(llIDX) &&
            IsValidIndex(hhIDX);
        if (!result)
        {
            return result;
        }

        //
        int hhDiff = (hhIDX - bar.Index());
        int llDiff = (llIDX - bar.Index());

        //
        // Bool Get Boundary Direction ...

        //
        // Check Price Change Rate ...

        //
        bool isBullish =
            //
            hhDiff <= 1 &&
            llIDX > hhIDX &&
            priceChangePercent > 0 &&
            priceChangePercent >= minPriceChangePercent &&
            currVelocity > (velocitiesAvg * rangeExtendMultiplier)
            //
            ;

        //
        bool isBearish =
            //
            llDiff <= 1 &&
            hhIDX > llIDX &&
            priceChangePercent < 0 &&
            MathAbs(priceChangePercent) >= minPriceChangePercent &&
            currVelocity > (velocitiesAvg * rangeExtendMultiplier)
            //
            ;

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            rootIDX =
                isBullish
                    ? llIDX
                    : hhIDX;

            //
            dir = isBullish
                      ? X_DIRECTION_BULLISH
                      : X_DIRECTION_BEARISH;
        }

        //
        return result;
    }

    //
    // Actions ...

    /**
     * Check a Bar is End of an OrderBlock Bar or Not ...
     *
     * @param  bar: XOHCL ...
     * @param  box: XBoxZone ...
     * @param  forceFVGBarType: bool, Force all Bars must be Same as Direction ...
     * @param  forceOBTwoBar: bool, Force Two Bars of OB Start must be InDirection ...
     *
     * @return ( bool )
     */
    bool IsOB(
        XOHCL &bar,
        XBoxZone &box,
        bool forceFVGBarType = false,
        bool forceOBTwoBar = false //
    )
    {
        //
        bool result = false;

        //
        box.Clean();

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Bar Must be FVG ...
        result = IsFVG(
            bar,
            box,
            forceFVGBarType //
        );
        if (!result)
        {
            //
            box.Clean();

            //
            return result;
        }

        //
        box.type = "XOB";

        //
        bool isBullish = box.IsBullish();
        bool isBearish = box.IsBearish();

        //
        // Order Block Includes an FVG which Starts by atleast One
        // or More Indirectional Bars ...
        XOHCL fvgLastBar;
        XOHCL fvgLastBarPrev;
        XOHCL fvgLastBarPrev2;

        //
        int barIndex = bar.Index();
        int fvgLastBarIndex = barIndex + 3;
        result = fvgLastBar.Init(
            bar.symbol,
            bar.period,
            fvgLastBarIndex //
        );
        result =
            result &&
            fvgLastBar.GetPreviousBar(fvgLastBarPrev);
        result =
            result &&
            fvgLastBarPrev.GetPreviousBar(fvgLastBarPrev2);
        if (!result)
        {
            //
            box.Clean();
            fvgLastBar.Clean();
            fvgLastBarPrev.Clean();
            fvgLastBarPrev2.Clean();

            //
            return result;
        }

        //
        // Check FVG Last Bar ...

        //
        bool isBullishBlock =
            isBullish &&
            (fvgLastBar.IsBearish() ||
             (fvgLastBar.IsBullish() &&
              fvgLastBarPrev.IsBearish()));

        //
        bool isBearishBlock =
            isBearish &&
            (fvgLastBar.IsBullish() ||
             (fvgLastBar.IsBearish() &&
              fvgLastBarPrev.IsBullish()));

        //
        // Update Box ...

        //
        if (isBullishBlock ||
            isBearishBlock)
        {
            //
            box.from = fvgLastBar.time;

            //
            box.lower = MathMin(box.lower, fvgLastBar.low);
            box.upper = MathMax(box.upper, fvgLastBar.high);
        }

        //
        // Apply Force Two Bar ...
        if (forceOBTwoBar)
        {
            //
            isBullishBlock =
                isBullishBlock &&
                fvgLastBarPrev.IsBearish();

            //
            isBearishBlock =
                isBearishBlock &&
                fvgLastBarPrev.IsBullish();

            //
            // Update Box ...
            if (isBullishBlock ||
                isBearishBlock)
            {
                //
                box.from = fvgLastBarPrev.time;
                box.lower = MathMin(box.lower, fvgLastBarPrev.low);
                box.upper = MathMax(box.upper, fvgLastBarPrev.high);
            }
        }

        //
        result = isBullishBlock ||
                 isBearishBlock;
        if (!result)
        {
            //
            box.Clean();
            fvgLastBar.Clean();
            fvgLastBarPrev.Clean();

            //
            return result;
        }

        //
        result = box.IsValid();

        //
        // Cleanup Resources ...

        //
        fvgLastBar.Clean();
        fvgLastBarPrev.Clean();

        //
        return result;
    }

    /**
     * Check a Bar is End of an FVG Bar or Not ...
     *
     * @param  bar: XOHCL ...
     * @param  box: XBoxZone ...
     * @param  forceType: bool, Force all Bars must be Same as Direction ...
     *
     * @return ( bool )
     */
    bool IsFVG(
        XOHCL &bar,
        XBoxZone &box,
        bool forceType = false //
    )
    {
        //
        bool result = false;

        //
        box.Clean();

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        XOHCL p2Bar;
        result = bar.GetPreviousBar(pBar);
        result = result &&
                 pBar.GetPreviousBar(p2Bar);
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        bool isBullish = p2Bar.high < bar.low;
        bool isBearish = p2Bar.low > bar.high;

        //
        result = isBullish ||
                 isBearish;

        //
        // Apply Force Type ...
        if (result)
        {
            //
            if (isBullish)
            {
                //
                result =
                    bar.IsBullish() &&
                    pBar.IsBullish() &&
                    p2Bar.IsBullish();
            }
            else if (isBearish)
            {
                //
                result =
                    bar.IsBearish() &&
                    pBar.IsBearish() &&
                    p2Bar.IsBearish();
            }
        }

        //
        if (result)
        {
            //
            box.to = bar.time;
            box.from = p2Bar.time;
            box.symbol = bar.symbol;
            box.period = bar.period;
            box.dir = isBullish
                          ? X_DIRECTION_BULLISH
                          : X_DIRECTION_BEARISH;
            box.upper = isBullish
                            ? bar.low
                            : p2Bar.low;
            box.lower = isBullish
                            ? p2Bar.high
                            : bar.high;
            box.type = "XFVG";
        }

        //
        pBar.Clean();
        p2Bar.Clean();

        //
        result = box.IsValid();

        //
        return result;
    }

    /**
     * Check a Bar is a Trigger Box or Not ...
     *
     * @param  bar: XOHCL instance Referernce, Specified Start Bar to Check ...
     * @param  trigger: XBoxZone instance Reference, Holds Detected Trigger Bar ...
     * @param  ob: XBoxZone instance Reference, Holds Detected Trigger Bar's OB ...
     * @param  fvg: XBoxZone instance Reference, Holds Detected Trigger Bar's FVG ...
     * @param  rLiquidity: XBoxZone instance Reference, Holds Detected Trigger Bar's Reversal Side Liquidity ...
     * @param  fLiquidity: XBoxZone instance Reference, Holds Detected Trigger Bar's Following Side Liquidity ...
     * @param  swingBar: XOHCL instance Referernce, Holds Detected Trigger Bar's Swing ...
     * @param  loopback: int, Loopback Bars Count ...
     * @param  maxAllowedRange: double, Max Allowed Trigger's OB Range ...
     * @param  minAllowedBlockEdgeLength: int, Min Allowed Block Edge Validation Length ...
     * @param  forceOBOppositBar: bool, Force OB to Start by Opposite Bar ...
     * @param  forceFVGBarType: bool, Force FVG to Has Same Bar Type ...
     * @param  forceGapBarBreakout: bool, Force Gap Bar Breakout OB Edge ...
     * @param  forceHasSwingBar: bool, Force Has Swing Bar ...
     * @param  forceHasLiquidity: bool, Force Has Following Liquidity ...
     * @param  forceHasReversalLiquidity: bool, Force Has Reversal Liquidity ...
     *
     * @return ( bool )
     */
    bool IsTrigger(
        XOHCL &bar,
        XBoxZone &trigger,
        XBoxZone &ob,
        XBoxZone &fvg,
        XBoxZone &rLiquidity,
        XBoxZone &fLiquidity,
        XOHCL &swingBar,
        int loopback = 10,
        double maxAllowedRange = 0.0,
        int minAllowedBlockEdgeLength = 0,
        bool forceOBOppositBar = true,
        bool forceFVGBarType = true,
        bool forceGapBarBreakout = true,
        bool forceHasSwingBar = true,
        bool forceHasLiquidity = true,
        bool forceHasReversalLiquidity = true //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        ob.Clean();
        fvg.Clean();
        trigger.Clean();
        swingBar.Clean();
        rLiquidity.Clean();
        fLiquidity.Clean();
        loopback = NormalizeInt(loopback, 5);

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        XOHCL tmpBar;
        XOHCL tmpPBar;
        ENUM_X_DIRECTION iDir = X_DIRECTION_NONE;

        //
        result = bar.GetPreviousBar(pBar);

        //
        // Detect Base Order Block ...
        result =
            result &&
            //
            // Order Block Detection ...
            IsOB(
                pBar,
                ob,
                forceFVGBarType,
                forceOBOppositBar //
                ) &&
            //
            // Gap Detection ...
            IsFVG(
                pBar,
                fvg,
                forceFVGBarType //
            );
        if (result)
        {
            //
            trigger = ob;
            trigger.type = "XTRG";
        }

        //
        // Reading Requirements ...

        //
        double points = GetPoints(bar.symbol);

        //
        bool isBullish = ob.IsBullish();
        bool isBearish = ob.IsBearish();

        //
        int toIDX = trigger.ToIndex();
        int fromIDX = trigger.FromIndex();

        //
        // Validate Gaps Bar Breakeout ...
        if (result &&
            forceGapBarBreakout)
        {
            //
            // Extract Gap Bar ...
            tmpBar.Clean();
            tmpPBar.Clean();
            result =
                //
                // Retrieve Bars ...
                fvg.ToBar(tmpBar) &&
                tmpBar.GetPreviousBar(tmpPBar) &&
                //
                // Validate Gap Bar is Momentum Bar ...
                IsMomentum(
                    tmpPBar,
                    iDir,
                    1 //
                    ) &&
                iDir == fvg.dir &&
                //
                // Validate ...
                (isBullish
                     ? tmpPBar.close > ob.upper
                     : tmpPBar.close < ob.lower);

            //
            tmpBar.Clean();
            tmpPBar.Clean();
        }

        //
        // Validate Range ...
        if (result &&
            maxAllowedRange > 0)
        {
            //
            double range = ob.GetRange();
            result = range <= (maxAllowedRange * points);
        }

        //
        // Validating Swing Bar ...
        if (result &&
            forceHasSwingBar)
        {
            //
            int swingIDX = -1;
            result = ob.FromBar(tmpBar);
            if (result)
            {
                //
                // Detect Swing Bar Index ...
                if (isBullish)
                {
                    swingIDX = tmpBar.FindLowestIndex(loopback, MODE_LOW);
                }
                else if (isBearish)
                {
                    swingIDX = tmpBar.FindHighestIndex(loopback, MODE_HIGH);
                }
                result = IsValidIndex(swingIDX);
                if (result)
                {
                    //
                    // Initialize Swing Bar ...
                    result = swingBar.Init(
                        bar.symbol,
                        bar.period,
                        swingIDX //
                    );
                }

                //
                // Update Trigger Block based on Swing Bar ...
                result = swingBar.IsValid();
                if (result)
                {
                    //
                    // Update From ...
                    trigger.from = ((datetime)MathMin((int)trigger.from, (int)swingBar.time));

                    //
                    // Update Edge ...
                    if (isBullish)
                    {
                        trigger.lower = MathMin(trigger.lower, swingBar.GetDown());
                    }
                    else if (isBearish)
                    {
                        trigger.upper = MathMax(trigger.upper, swingBar.GetUp());
                    }
                }
            }

            //
            tmpBar.Clean();
        }

        //
        // Validate Min Block Edge ...
        if (result &&
            minAllowedBlockEdgeLength > 0)
        {
            //
            fromIDX = trigger.FromIndex();
            if (result)
            {
                //
                // Looping Back ...
                for (int i = fromIDX; i < fromIDX + minAllowedBlockEdgeLength; i++)
                {
                    //
                    // Initialize i Bar ...
                    result = tmpBar.Init(
                        bar.symbol,
                        bar.period,
                        i //
                    );
                    result =
                        result &&
                        (isBullish
                             ? tmpBar.low > trigger.lower
                             : tmpBar.high > trigger.upper);
                    if (!result)
                    {
                        break;
                    }

                    //
                    tmpBar.Clean();
                }
                result = tmpBar.IsValid();

                //
                // Update Trigger Block ...
                if (result)
                {
                    trigger.from = ((datetime)(MathMin((int)trigger.from, (int)tmpBar.time)));
                }
            }

            //
            tmpBar.Clean();
        }

        //
        // Validating Liquidity ...
        if (result &&
            (forceHasLiquidity ||
             forceHasReversalLiquidity))
        {
            //
            // Try to Detect Liquidity ...
            toIDX = ob.ToIndex();
            for (int i = toIDX; i < toIDX + (loopback * 10); i++)
            {
                //
                // Initialize i Bar ...
                result = tmpBar.Init(
                    bar.symbol,
                    bar.period,
                    i //
                );
                if (!result)
                {
                    break;
                }

                //
                // Detect Liquidity ...
                bool isRejected =
                    result &&
                    IsRejected(
                        tmpBar,
                        iDir,
                        false, // Force Bar Type ...
                        true   // Force Fibo Pressure ...
                    );
                bool isBullishRejected =
                    isRejected &&
                    IsBullish(iDir);
                bool isBearishRejected =
                    isRejected &&
                    IsBearish(iDir);

                //
                // Validate Liquidities ...
                if (isBullish)
                {
                    //
                    if (isBullishRejected &&
                        forceHasLiquidity &&
                        !fLiquidity.IsValid() &&
                        tmpBar.low < trigger.lower)
                    {
                        //
                        // Filling Following Liquidity ...
                        FillLiquidity(
                            tmpBar,
                            trigger.dir,
                            fLiquidity,
                            trigger.to //
                        );
                    }

                    //
                    if (isBearishRejected &&
                        !rLiquidity.IsValid() &&
                        forceHasReversalLiquidity &&
                        tmpBar.high > trigger.upper)
                    {
                        //
                        // Filling Revrsal Liquidity ...
                        FillLiquidity(
                            tmpBar,
                            Opposit(trigger.dir),
                            rLiquidity,
                            trigger.to //
                        );
                    }
                }
                else if (isBearish)
                {
                    //
                    if (isBearishRejected &&
                        forceHasLiquidity &&
                        !fLiquidity.IsValid() &&
                        tmpBar.high > trigger.upper)
                    {
                        //
                        // Filling Following Liquidity ...
                        FillLiquidity(
                            tmpBar,
                            trigger.dir,
                            fLiquidity,
                            trigger.to //
                        );
                    }

                    //
                    if (isBullishRejected &&
                        !rLiquidity.IsValid() &&
                        forceHasReversalLiquidity &&
                        tmpBar.low < trigger.lower)
                    {
                        //
                        // Filling Reversal Liquidity ...
                        FillLiquidity(
                            tmpBar,
                            Opposit(trigger.dir),
                            rLiquidity,
                            trigger.to //
                        );
                    }
                }

                //
                if ((!forceHasLiquidity
                         ? true
                         : fLiquidity.IsValid()) &&
                    (!forceHasReversalLiquidity
                         ? true
                         : rLiquidity.IsValid()))
                {
                    break;
                }

                //
                tmpBar.Clean();
            }
            tmpBar.Clean();

            //
            result = (!forceHasLiquidity
                          ? true
                          : fLiquidity.IsValid()) &&
                     (!forceHasReversalLiquidity
                          ? true
                          : rLiquidity.IsValid());
        }

        //
        // Cleanup Resources ...

        //
        pBar.Clean();
        tmpBar.Clean();
        tmpPBar.Clean();

        //
        if (!result)
        {
            //
            ob.Clean();
            fvg.Clean();
            trigger.Clean();
            swingBar.Clean();
            rLiquidity.Clean();
            fLiquidity.Clean();

            //
            return result;
        }

        //
        return result;
    }

    //
    // Candlestick ...

    /**
     * Check Specified Bar is Doji Pattern or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  shadowMultiplier: double, Specified Shadow Multiplier ...
     *
     * @return ( bool )
     */
    bool IsDoji(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double shadowMultiplier = 1.5 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        shadowMultiplier = NormalizeDouble(shadowMultiplier, 1.5, 3);

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        double body = bar.GetBody();
        double range = bar.GetRange();

        //
        result =
            body > 0 &&
                    range > 0
                ? (body / range < 0.1)
                : false;
        if (result)
        {
            //
            dir =
                bar.GetLowShadow() >= (bar.GetHighShadow() * shadowMultiplier)
                    ? X_DIRECTION_BULLISH
                : bar.GetHighShadow() >= (bar.GetLowShadow() * shadowMultiplier)
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
            //
            result = HasDirection(dir);
        }

        //
        return result;
    }

    /**
     * Check Specified Bar is Swing or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  loopback: int, Specified Loopback ...
     *
     * @return ( bool )
     */
    bool IsSwing(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 7 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 1);

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        XOHCL ppBar;
        result = pBar.GetPreviousBar(ppBar);
        if (!result)
        {
            //
            pBar.Clean();
            ppBar.Clean();
            return result;
        }

        //
        bool isBullish =
            //
            pBar.low < bar.low &&
            pBar.low < ppBar.low &&
            bar.GetDown() > pBar.low
            //
            ;

        //
        bool isBearish =
            //
            pBar.high > bar.high &&
            pBar.high > ppBar.high &&
            bar.GetUp() < pBar.high
            //
            ;

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;

            //
            ppBar.Clean();
            int pBarIDX = pBar.Index();
            for (int i = pBarIDX + 1; i < pBarIDX + loopback; i++)
            {
                //
                bool has = ppBar.Init(
                    pBar.symbol,
                    pBar.period,
                    i //
                );

                //
                isBullish =
                    has &&
                    isBullish &&
                    pBar.low < ppBar.low;

                //
                isBearish =
                    has &&
                    isBearish &&
                    pBar.high > ppBar.high;
            }

            //
            result = isBullish ||
                     isBearish;
            if (!result)
            {
                dir = X_DIRECTION_NONE;
            }
        }

        //
        // Cleanup Resources ...

        //
        pBar.Clean();
        ppBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar's Prev Bar is Simple Swing or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to it's Prev Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     *
     * @return ( bool )
     */
    bool IsSimpleSwing(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;

        //
        // Valdate Args ...
        result = bar.IsValid();

        //
        // Reading Requirements ...
        XOHCL cBar;
        XOHCL pBar;
        XOHCL p2Bar;
        result =
            bar.GetPreviousBar(cBar);
        result =
            result &&
            cBar.GetPreviousBar(pBar);
        result =
            result &&
            pBar.GetPreviousBar(p2Bar);
        if (!result)
        {
            //
            cBar.Clean();
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        // Now we Have to Check pBar is Swing Low or High ...

        //
        bool isPBarSwingLow =
            pBar.low < cBar.low &&
            pBar.low < p2Bar.low;

        //
        bool isPBarSwingHigh =
            pBar.high > cBar.high &&
            pBar.high > p2Bar.high;

        //
        result = isPBarSwingLow ||
                 isPBarSwingHigh;
        if (result)
        {
            //
            dir = isPBarSwingLow
                      ? X_DIRECTION_BULLISH
                      : X_DIRECTION_BEARISH;
        }

        //
        // Cleanup Resourses ...

        //
        cBar.Clean();
        pBar.Clean();
        p2Bar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Hammer Pattern or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  bodyMultiplier: double, Specified Body Multiplier ...
     *
     * @return ( bool )
     */
    bool IsHammer(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double bodyMultiplier = 2.0 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        bodyMultiplier = NormalizeDouble(bodyMultiplier, 1.5, 3);

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        double body = bar.GetBody();
        double lShadow = bar.GetLowShadow();
        double hShadow = bar.GetHighShadow();

        //
        bool isBullish =
            hShadow < body &&
            lShadow > (bodyMultiplier * body);

        //
        bool isBearish =
            lShadow < body &&
            hShadow > (bodyMultiplier * body);

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        return result;
    }

    /**
     * Check Specified Bar is Engulfed Prev Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     *
     * @return ( bool )
     */
    bool IsEngulfed(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        result = bar.GetBody() > pBar.GetBody();
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        bool isBullish =
            bar.IsBullish() &&
            pBar.IsBearish() &&
            bar.GetDown() <= pBar.GetDown() &&
            bar.GetUp() > pBar.GetUp();

        //
        bool isBearish =
            bar.IsBearish() &&
            pBar.IsBullish() &&
            bar.GetUp() >= pBar.GetUp() &&
            bar.GetDown() < pBar.GetDown();

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        // Cleanup Resources ...

        //
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Momentum Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  forceBarType: bool, Force Apply Bar Type ...
     * @param  forceFiboPressure: bool, Force Apply Bar Type ...
     *
     * @return ( bool )
     */
    bool IsRejected(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        bool forceBarType = true,
        bool forceFiboPressure = true //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        bool isBullish =
            //
            bar.low < pBar.low &&
            bar.GetShadows() > bar.GetBody() &&
            bar.GetLowShadow() > bar.GetBody() &&
            bar.GetLowShadow() > bar.GetHighShadow()
            //
            ;

        //
        bool isBearish =
            //
            bar.high > pBar.high &&
            bar.GetShadows() > bar.GetBody() &&
            bar.GetHighShadow() > bar.GetBody() &&
            bar.GetHighShadow() > bar.GetLowShadow()
            //
            ;

        //
        result = isBullish ||
                 isBearish;
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Check Force Type ...
        if (forceBarType)
        {
            //
            isBullish =
                isBullish &&
                bar.IsBullish();

            //
            isBearish =
                isBearish &&
                bar.IsBearish();

            //
            result = isBullish ||
                     isBearish;
            if (!result)
            {
                //
                pBar.Clean();
                return result;
            }
        }

        //
        // Check Force Fibo Pressure ...
        if (forceFiboPressure)
        {
            //
            bool hasBullishFiboPressure =
                HasFiboPressure(
                    bar,
                    X_DIRECTION_BULLISH,
                    X_FIBO_LEVEL_236 //
                );

            //
            bool hasBearishFiboPressure =
                HasFiboPressure(
                    bar,
                    X_DIRECTION_BEARISH,
                    X_FIBO_LEVEL_236 //
                );

            //
            isBullish =
                isBullish &&
                hasBullishFiboPressure;

            //
            isBearish =
                isBearish &&
                hasBearishFiboPressure;
        }

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Momentum Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  loopback: int, Specified Loopback ...
     *
     * @return ( bool )
     */
    bool IsMomentum(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 2 //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 1);

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Calculate HH and LL ...
        double hh = 0;
        double ll = 0;
        int hhIDX = -1;
        int llIDX = -1;
        result = CalculateLoopbackHLData(
            hhIDX,
            hh,
            llIDX,
            ll,
            bar,
            loopback // s
        );
        result =
            result &&
            ll > 0 &&
            hh > ll &&
            IsValidIndex(llIDX) &&
            IsValidIndex(hhIDX);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        bool isBullish =
            bar.low > ll &&
            bar.close >= hh &&
            bar.IsBullish() &&
            bar.low > pBar.low &&
            bar.high > pBar.high &&
            bar.GetBody() > pBar.GetBody() &&
            bar.GetRange() > pBar.GetRange() &&
            bar.GetShadows() < pBar.GetShadows();

        //
        bool isBearish =
            bar.high < hh &&
            bar.close <= ll &&
            bar.IsBearish() &&
            bar.low < pBar.low &&
            bar.high < pBar.high &&
            bar.GetBody() > pBar.GetBody() &&
            bar.GetRange() > pBar.GetRange() &&
            bar.GetShadows() < pBar.GetShadows();

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Pullback or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Specified Direction ...
     * @param  loopback: int, Specified Loopback ...
     * @param  forceBody: bool, Force Using Body ...
     *
     * @return ( bool )
     */
    bool IsPullback(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 3,
        bool forceBody = false //
    )
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 3);

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Bar Must Engulfed ...
        result = IsEngulfed(
            bar,
            dir //
        );
        if (!result)
        {
            return result;
        }

        //
        // Calculate HH and LL ...
        double hh = 0;
        double ll = 0;
        int hhIDX = -1;
        int llIDX = -1;
        result = CalculateLoopbackHLData(
            hhIDX,
            hh,
            llIDX,
            ll,
            bar,
            loopback //
        );
        result =
            result &&
            ll > 0 &&
            hh > ll &&
            IsValidIndex(llIDX) &&
            IsValidIndex(hhIDX);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish =
            bar.high > hh &&
            bar.low > ll;

        //
        bool isBearish =
            bar.high < hh &&
            bar.low < ll;

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            if (forceBody)
            {
                //
                isBullish =
                    isBullish &&
                    bar.GetUp() > hh &&
                    bar.GetDown() > ll &&
                    llIDX == bar.Index() + 1;

                //
                isBearish =
                    isBearish &&
                    bar.GetUp() < hh &&
                    bar.GetDown() < ll &&
                    hhIDX == bar.Index() + 1;

                //
                result = isBullish ||
                         isBearish;
            }

            //
            if (result)
            {
                //
                dir =
                    isBullish
                        ? X_DIRECTION_BULLISH
                        : X_DIRECTION_BEARISH;
            }
        }

        //
        return result;
    }

    /**
     * Check Specified Bar is Sequence Edge Breaker Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Holds Founded Direction ...
     * @param  loopback: int, Specified Loopback ...
     *
     * @return ( bool )
     */
    bool IsSeqBreakEdge(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 3 //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 3);

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = false;
        bool isBearish = false;

        //
        // Check Has Sequence or not ...
        XOHCL pBar;
        XOHCL iBar;
        XOHCL iPBar;
        bool has = false;
        int index = bar.Index();
        int start = index + 1;
        int end = start + loopback - 1;
        bool hasBullishSeq = true;
        bool hasBearishSeq = true;
        for (int i = start; i < end; i++)
        {
            //
            // Initialize Indexed Bar(s) ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );
            has =
                has &&
                iBar.GetPreviousBar(iPBar);

            //
            // Bullish Sequence ...
            if (hasBullishSeq)
            {
                //
                hasBullishSeq =
                    has &&
                    iBar.low > iPBar.low;
            }

            //
            // Bearish Sequence ...
            if (hasBearishSeq)
            {
                //
                hasBearishSeq =
                    has &&
                    iBar.high < iPBar.high;
            }

            //
            if (!hasBullishSeq &&
                !hasBearishSeq)
            {
                break;
            }

            //
            iBar.Clean();
            iPBar.Clean();
        }
        iBar.Clean();
        iPBar.Clean();

        //
        // Check a Sequence Detect ...
        result = hasBullishSeq ||
                 hasBearishSeq;
        if (!result)
        {
            //
            pBar.Clean();
            iBar.Clean();
            iPBar.Clean();

            //
            return result;
        }

        //
        // Retrieve Previous Bar ...
        result = bar.GetPreviousBar(pBar);

        //
        // Now we Have Ensure has a Sequence ...

        //
        isBullish =
            result &&
            hasBearishSeq &&
            bar.high > pBar.high;

        //
        isBearish =
            result &&
            hasBullishSeq &&
            bar.low < pBar.low;

        //
        // Summarizing Result ...
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        // Cleanup Resources ...
        pBar.Clean();
        iBar.Clean();
        iPBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Sequence Type Breaker Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Holds Founded Direction ...
     * @param  loopback: int, Specified Loopback ...
     *
     * @return ( bool )
     */
    bool IsSeqBreakType(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 3 //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 3);

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = false;
        bool isBearish = false;

        //
        // Check Has Sequence or not ...
        XOHCL iBar;
        bool has = false;
        int index = bar.Index();
        int start = index + 1;
        int end = start + loopback;
        bool hasBullishSeq = true;
        bool hasBearishSeq = true;
        for (int i = start; i < end; i++)
        {
            //
            // Initialize Indexed Bar(s) ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );

            //
            // Bullish Sequence ...
            if (hasBullishSeq)
            {
                //
                hasBullishSeq =
                    has &&
                    iBar.IsBullish();
            }

            //
            // Bearish Sequence ...
            if (hasBearishSeq)
            {
                //
                hasBearishSeq =
                    has &&
                    iBar.IsBearish();
            }

            //
            if (!hasBullishSeq &&
                !hasBearishSeq)
            {
                break;
            }

            //
            iBar.Clean();
        }
        iBar.Clean();

        //
        // Check a Sequence Detect ...
        result = hasBullishSeq ||
                 hasBearishSeq;
        if (!result)
        {
            //
            iBar.Clean();

            //
            return result;
        }

        //
        // Now we Have Ensure has a Sequence ...

        //
        isBullish =
            result &&
            hasBearishSeq &&
            bar.IsBullish();

        //
        isBearish =
            result &&
            hasBullishSeq &&
            bar.IsBearish();

        //
        // Summarizing Result ...
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Fully Sequence Breker Bar or not ...
     *
     * @param  bar: XOHCL instance reference, Specified Bar to Check ...
     * @param  dir: ENUM_X_DIRECTION member, Holds Founded Direction ...
     * @param  loopback: int, Specified Loopback ...
     *
     * @return ( bool )
     */
    bool IsSeqBreak(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 3 //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        dir = X_DIRECTION_NONE;
        loopback = NormalizeInt(loopback, 3);

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Seq Edge ...
        ENUM_X_DIRECTION seqEdgeDir;
        bool isSeqEdgeBreaked = IsSeqBreakEdge(
            bar,
            seqEdgeDir //
        );

        //
        // Check Seq Type ...
        ENUM_X_DIRECTION seqTypeDir;
        bool isSeqTypeBreaked = IsSeqBreakType(
            bar,
            seqTypeDir //
        );

        //
        // Check Result ...
        result = isSeqEdgeBreaked &&
                 isSeqTypeBreaked &&
                 seqEdgeDir == seqTypeDir;
        if (result)
        {
            dir = seqEdgeDir;
        }

        //
        return result;
    }

    //
    //
    //

    /**
     * Check a Bar is Valid for Specified Direction of Positions ...
     *
     * @param  bar: XOHCL instance, Start Bar ...
     * @param  dir: ENUM_X_DIRECTION member, Provide Direction ...
     *
     * @return ( bool )
     */
    bool IsValidForTrigger(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        bool allowCond1 = true,
        bool allowCond2 = true,
        bool allowCond3 = true,
        bool allowCond4 = true,
        bool allowCond5 = true,
        bool allowCond6 = true,
        bool allowCond7 = true //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        dir = X_DIRECTION_NONE;

        //
        // Validate Args ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Prepare Requirements ...
        XOHCL pBar;
        XOHCL p2Bar;
        int loopback = 3;
        bool isBullish = false;
        bool isBearish = false;
        ENUM_X_DIRECTION iDir = X_DIRECTION_NONE;

        //
        // Retrieve Bar's Prev ...
        result = bar.GetPreviousBar(pBar);
        result = result &&
                 pBar.GetPreviousBar(p2Bar);

        //
        // Handle Detect Trigger Bar ...
        if (result)
        {
            //
            // Check States ...

            //
            // Rejected ...
            bool isRejected = IsRejected(
                pBar,
                iDir,
                false, // Force Type ...
                true   // Force Fibo Pressure ...
            );
            bool isBullishRejected =
                isRejected &&
                IsBullish(iDir);
            bool isBearishRejected =
                isRejected &&
                IsBearish(iDir);

            //
            // Simple Rejected ...
            bool isSimpleRejected = IsRejected(
                pBar,
                iDir,
                false, // Force Type ...
                false  // Force Fibo Pressure ...
            );
            bool isSimpleBullishRejected =
                isSimpleRejected &&
                IsBullish(iDir);
            bool isSimpleBearishRejected =
                isSimpleRejected &&
                IsBearish(iDir);

            //
            // Full Directional Rejection ...
            bool isFullRejected = IsRejected(
                pBar,
                iDir,
                true, // Force Type ...
                true  // Force Fibo Pressure ...
            );
            bool isFullBullishRejected =
                isFullRejected &&
                IsBullish(iDir);
            bool isFullBearishRejected =
                isRejected &&
                IsBearish(iDir);

            //
            // Engulfed ...
            bool isEngulfed = IsEngulfed(
                pBar,
                iDir //
            );
            bool isBullishEngulfed =
                isEngulfed &&
                IsBullish(iDir);
            bool isBearishEngulfed =
                isEngulfed &&
                IsBearish(iDir);

            //
            // Momentum ...
            bool isMomentum = IsMomentum(
                pBar,
                iDir //
            );
            bool isBullishMomentum =
                isMomentum &&
                IsBullish(iDir);
            bool isBearishMomentum =
                isMomentum &&
                IsBearish(iDir);

            //
            // Simple Swing ...
            bool isSwing = IsSimpleSwing(
                pBar,
                iDir //
            );
            bool isBullishSwing =
                isSwing &&
                IsBullish(iDir);
            bool isBearishSwing =
                isSwing &&
                IsBearish(iDir);

            //
            // Full Swing ...
            bool isFullSwing = IsSwing(
                pBar,
                iDir,
                5 //
            );
            bool isFullBullishSwing =
                isFullSwing &&
                IsBullish(iDir);
            bool isFullBearishSwing =
                isFullSwing &&
                IsBearish(iDir);

            //
            // Sequence Breaker ...
            bool isSeqBreak = IsSeqBreak(
                pBar,
                iDir,
                3 //
            );
            bool isBullishSeqBreak =
                isSeqBreak &&
                IsBullish(iDir);
            bool isBearishSeqBreak =
                isSeqBreak &&
                IsBearish(iDir);

            //
            // Now we have to Combine Conditions ...

            //
            // Cond 1 ...
            bool isBullishCond1 = false;
            bool isBearishCond1 = false;
            if (allowCond1)
            {
                //
                // Bullish ...
                isBullishCond1 =
                    //
                    isFullBullishRejected
                    //
                    ;

                //
                // Bearish ...
                isBearishCond1 =
                    //
                    isFullBearishRejected
                    //
                    ;
            }

            //
            // Cond 2 ...
            bool isBullishCond2 = false;
            bool isBearishCond2 = false;
            if (allowCond2)
            {
                //
                // Bullish ...
                isBullishCond2 =
                    //
                    isBullishSwing &&
                    isBullishRejected
                    //
                    ;

                //
                // Bearish ...
                isBearishCond2 =
                    //
                    isBearishSwing &&
                    isBearishRejected
                    //
                    ;
            }

            //
            // Cond 3 ...
            bool isBullishCond3 = false;
            bool isBearishCond3 = false;
            if (allowCond3)
            {
                //
                // Bullish ...
                isBullishCond3 =
                    //
                    isBullishSwing &&
                    (isBullishRejected ||
                     isBullishEngulfed ||
                     isBullishMomentum)
                    //
                    ;

                //
                // Bearish ...
                isBearishCond3 =
                    //
                    isBearishSwing &&
                    (isBearishRejected ||
                     isBearishEngulfed ||
                     isBearishMomentum)
                    //
                    ;
            }

            //
            // Cond 4 ...
            bool isBullishCond4 = false;
            bool isBearishCond4 = false;
            if (allowCond4)
            {
                //
                // Bullish ...
                isBullishCond4 =
                    //
                    isBullishSwing &&
                    isBullishSeqBreak &&
                    (isBullishRejected ||
                     isBullishEngulfed ||
                     isBullishMomentum)
                    //
                    ;

                //
                // Bearish ...
                isBearishCond4 =
                    //
                    isBearishSwing &&
                    isBearishSeqBreak &&
                    (isBearishRejected ||
                     isBearishEngulfed ||
                     isBearishMomentum)
                    //
                    ;
            }

            //
            // Cond 5 ...
            bool isBullishCond5 = false;
            bool isBearishCond5 = false;
            if (allowCond5)
            {
                //
                // Bullish ...
                isBullishCond5 =
                    //
                    isFullBullishSwing &&
                    (isBullishRejected ||
                     isBullishEngulfed ||
                     isBullishMomentum)
                    //
                    ;

                //
                // Bearish ...
                isBearishCond5 =
                    //
                    isFullBearishSwing &&
                    (isBearishRejected ||
                     isBearishEngulfed ||
                     isBearishMomentum)
                    //
                    ;
            }

            //
            // Cond 6 ...
            bool isBullishCond6 = false;
            bool isBearishCond6 = false;
            if (allowCond6)
            {
                //
                // Bullish ...
                isBullishCond6 =
                    //
                    p2Bar.IsBearish() &&
                    pBar.IsBullish() &&
                    pBar.low < p2Bar.low &&
                    pBar.high > p2Bar.high &&
                    pBar.GetDown() <= p2Bar.GetDown() &&
                    (isBullishEngulfed ||
                     isBullishMomentum ||
                     isSimpleBullishRejected)
                    //
                    ;

                //
                // Bearish ...
                isBearishCond6 =
                    //
                    p2Bar.IsBullish() &&
                    pBar.IsBearish() &&
                    pBar.high > p2Bar.high &&
                    pBar.low < p2Bar.low &&
                    pBar.GetUp() >= p2Bar.GetUp() &&
                    (isBearishEngulfed ||
                     isBearishMomentum ||
                     isSimpleBearishRejected)
                    //
                    ;
            }

            //
            // Cond 7 ...
            bool isBullishCond7 = false;
            bool isBearishCond7 = false;
            if (allowCond7)
            {
                //
                // Bullish ...
                isBullishCond7 =
                    //
                    p2Bar.IsBearish() &&
                    pBar.IsBullish() &&
                    pBar.low < p2Bar.low &&
                    pBar.high > p2Bar.high &&
                    pBar.GetUp() >= p2Bar.GetUp()
                    //
                    ;

                //
                // Bearish ...
                isBearishCond7 =
                    //
                    p2Bar.IsBullish() &&
                    pBar.IsBearish() &&
                    pBar.high > p2Bar.high &&
                    pBar.low < p2Bar.low &&
                    pBar.GetDown() <= p2Bar.GetDown()
                    //
                    ;
            }

            //
            // Summarizie Result ...

            //
            isBullish =
                //
                isBullishCond1 ||
                isBullishCond2 ||
                isBullishCond3 ||
                isBullishCond4 ||
                isBullishCond5 ||
                isBullishCond6 ||
                isBullishCond7
                //
                ;

            //
            isBearish =
                //
                isBearishCond1 ||
                isBearishCond2 ||
                isBearishCond3 ||
                isBearishCond4 ||
                isBearishCond5 ||
                isBearishCond6 ||
                isBearishCond7
                //
                ;
        }

        //
        // Summarize Result ...
        result = (isBullish &&
                  !isBearish) ||
                 (isBearish &&
                  !isBullish);
        if (result)
        {
            //
            dir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
        }

        //
        // Cleanup Resources ...
        pBar.Clean();
        p2Bar.Clean();

        //
        return result;
    }

    //
  protected:
    //

    //
  private:
    //

    //
    // Props ...

    //
    // Actions ...

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-chart.helper.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCChartHelper
// Description: Chart Helper Tools ...
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"

//
// Implementation ...
class XCChartHelper : public XCBase
{
    //
    // Public ...
  public:
    //

    //
    // Constructor ...
    XCChartHelper(
        ulong _chartId = 0,
        string _symbol = NULL,
        ENUM_TIMEFRAMES _period = NULL)
    {
        //
        bool canSetSymbolPeriod =
            IsSpecifiedValid(_symbol) &&
            IsSpecifiedValid(_period);

        //
        // Normalize ...
        if (!canSetSymbolPeriod)
        {
            //
            mChartID = NormalizeChartId(_chartId);
            mSymbol = NormalizeSymbol(_symbol);
            mPeriod = NormalizePeriod(_period);
        }
        else
        {
            //
            SetSymbolPeriod(mSymbol, mPeriod);
        }
    }

    //
    // Desconstructor ...
    ~XCChartHelper()
    {
    }

    //
    // Actioins ...

    /**
     * Redraw Specified Chart ...
     */
    void Redraw()
    {
        ChartRedraw(mChartID);
    }

    /**
     * Chart Navigation ...
     *
     * @param  position: ENUM_CHART_POSITION
     * @param  shift: int
     *
     * @return ( bool )
     */
    bool Navigate(
        ENUM_CHART_POSITION position,
        int shift //
    )
    {
        return (ChartNavigate(mChartID, position, shift));
    }

    /**
     * Apply a Template to Chart ...
     *
     * @param  filename: string file path ...
     *
     * @return ( bool )
     */
    bool ApplyTemplate(string filename)
    {
        return (ChartApplyTemplate(mChartID, filename));
    }

    /**
     * Add Specified Indicator to Chart ...
     *
     * @param  subWindow: int ...
     * @param  handle: int ...
     *
     * @return ( bool )
     */
    bool IndicatorAdd(int subWindow, int handle)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartIndicatorAdd(mChartID, subWindow, handle));
    }

    /**
     * Delete Specified Indicator from Chart ...
     *
     * @param  subWindow: int ...
     * @param  name: string ...
     *
     * @return ( bool )
     */
    bool IndicatorDelete(int subWindow, string name)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartIndicatorDelete(mChartID, subWindow, name));
    }

    /**
     * Count all Charts Indicator ...
     *
     * @param  subWindow: int ...
     *
     * @return ( int )
     */
    int IndicatorsTotal(int subWindow)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartIndicatorsTotal(mChartID, subWindow));
    }

    /**
     * Get Specified Indexed Indicator's Name ...
     *
     * @param  subWindow: int ...
     * @param  index: int ...
     *
     * @return ( string )
     */
    string IndicatorName(int subWindow, int index)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartIndicatorName(mChartID, subWindow, index));
    }

    /**
     * Set Chart Symbol, Period ...
     *
     * @param  symbol: string ...
     * @param  period: ENUM_TIMEFRAMES ...
     *
     * @return ( bool )
     */
    bool SetSymbolPeriod(
        string symbol,
        ENUM_TIMEFRAMES period //
    )
    {
        bool result = (ChartSetSymbolPeriod(mChartID, symbol, period));
        if (result)
        {
            //
            mSymbol = symbol;
            mPeriod = period;
        }

        //
        return result;
    }

    /**
     * Take an Screenshot of Chart ...
     *
     * @param  filename: string file path ...
     * @param  width: int ...
     * @param  height: int ...
     * @param  align_mode: ENUM_ALIGN_MODE ...
     *
     * @return ( bool )
     */
    bool ScreenShot(
        const string filename,
        const int width,
        const int height,
        const ENUM_ALIGN_MODE align_mode //
    ) const
    {
        return (ChartScreenShot(mChartID, filename, width, height, align_mode));
    }

    /**
     * Writing parameters of chart to file ...
     **/
    bool Save(const int file_handle)
    {
        //
        string work_str;
        int work_int;

        //
        bool has = false;

        //
        // Validate ...
        if (file_handle == INVALID_HANDLE || mChartID == -1)
        {
            return has;
        }

        //
        // Write start marker - 0xFFFFFFFFFFFFFFFF ...
        has = FileWriteLong(file_handle, -1) == sizeof(long);
        if (!has)
        {
            return has;
        }

        //
        // Write chart type ...
        has = FileWriteInteger(file_handle, Type(), INT_VALUE) == INT_VALUE;
        if (!has)
        {
            return has;
        }

        //
        // Write chart symbol ...
        work_str = Symbol();
        work_int = StringLen(work_str);
        has = FileWriteInteger(file_handle, work_int, INT_VALUE) == INT_VALUE;
        if (!has)
        {
            return has;
        }
        if (work_int != 0)
        {
            //
            has = FileWriteString(file_handle, work_str, work_int) == work_int;
            if (!has)
            {
                return has;
            }
        }

        //
        // Write Period of chart  ...
        has = FileWriteInteger(file_handle, Period(), INT_VALUE) == sizeof(int);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "Mode" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_MODE), INT_VALUE) == sizeof(int);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "Foreground" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_FOREGROUND), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "Shift" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHIFT), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShiftSize" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHIFT), INT_VALUE) == sizeof(int);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "AutoScroll" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_AUTOSCROLL), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "Scale" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALE), INT_VALUE) == sizeof(int);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ScaleFix" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALEFIX), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ScaleFix_11" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALEFIX_11), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "FixedMax" property ...
        has = FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_FIXED_MAX)) == sizeof(double);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "FixedMin" property ...
        has = FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_FIXED_MIN)) == sizeof(double);
        if (!has)
        {
            return has;
        }

        //
        // Write the "ScalePPB" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALE_PT_PER_BAR), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "PointsPerBar" property ...
        has = FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_POINTS_PER_BAR)) == sizeof(double);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowOHLC" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_OHLC), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowLineBid" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_BID_LINE), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowLineAsk" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_ASK_LINE), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowLastLine" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_LAST_LINE), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowPeriodSep" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_PERIOD_SEP), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowGrid" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_GRID), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowVolumes" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_VOLUMES), INT_VALUE) == sizeof(int);
        if (!has)
        {
            return has;
        }

        //
        // Write value of the "ShowObjectDescr" property ...
        has = FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_OBJECT_DESCR), CHAR_VALUE) == sizeof(char);
        if (!has)
        {
            return has;
        }

        //
        return has;
    }

    /**
     * Load Specified Chart File ...
     *
     * @param  file_handle: int ...
     *
     * @return ( bool )
     */
    bool Load(const int file_handle)
    {
        //
        string work_str;
        int work_int;

        //
        bool has = false;

        //
        // Validate ...
        if (file_handle == INVALID_HANDLE || mChartID == -1)
        {
            return has;
        }

        //
        // Read and checking start marker - 0xFFFFFFFFFFFFFFFF ...
        if (FileReadLong(file_handle) != -1)
            return has;

        //
        // Read and checking chart type ...
        if (FileReadInteger(file_handle, INT_VALUE) != Type())
            return has;

        //
        // Read chart symbol ...
        work_int = FileReadInteger(file_handle);
        if (work_int != 0)
        {
            work_str = FileReadString(file_handle, work_int);
        }
        else
        {
            work_str = "";
        }

        //
        // Read chart period ...
        work_int = FileReadInteger(file_handle);
        SetSymbolPeriod(work_str, (ENUM_TIMEFRAMES)work_int);

        //
        // Read value of the "Mode" property ...
        has = ChartSetInteger(mChartID, CHART_MODE, FileReadInteger(file_handle, INT_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "Foreground" property ...
        has = ChartSetInteger(mChartID, CHART_FOREGROUND, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "Shift" property ...
        has = ChartSetInteger(mChartID, CHART_SHIFT, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "ShiftSize" property ...
        has = ChartSetInteger(mChartID, CHART_SHIFT, FileReadInteger(file_handle, INT_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "AutoScroll" property ...
        has = ChartSetInteger(mChartID, CHART_AUTOSCROLL, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "Scale" property ...
        has = ChartSetInteger(mChartID, CHART_SCALE, FileReadInteger(file_handle, INT_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "ScaleFix" property ...
        has = ChartSetInteger(mChartID, CHART_SCALEFIX, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Read value of the "ScaleFix_11" property ...
        has = ChartSetInteger(mChartID, CHART_SCALEFIX_11, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "FixedMax" property ...
        has = ChartSetDouble(mChartID, CHART_FIXED_MAX, FileReadDatetime(file_handle));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "FixedMin" property ...
        has = ChartSetDouble(mChartID, CHART_FIXED_MIN, FileReadDatetime(file_handle));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ScalePPB" property ...
        has = ChartSetInteger(mChartID, CHART_SCALE_PT_PER_BAR, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "PointsPerBar" property ...
        has = ChartSetDouble(mChartID, CHART_POINTS_PER_BAR, FileReadDatetime(file_handle));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowOHLC" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_OHLC, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowLineBid" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_BID_LINE, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowLineAsk" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_ASK_LINE, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowLastLine" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_LAST_LINE, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowPeriodSep" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_PERIOD_SEP, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowGrid" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_GRID, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowVolumes" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_VOLUMES, FileReadInteger(file_handle, INT_VALUE));
        if (!has)
        {
            return has;
        }

        //
        // Rread value of the "ShowObjectDescr" property ...
        has = ChartSetInteger(mChartID, CHART_SHOW_OBJECT_DESCR, FileReadInteger(file_handle, CHAR_VALUE));
        if (!has)
        {
            return has;
        }

        //
        return has;
    }

    ulong Open()
    {
        //
        mChartID = ChartOpen(mSymbol, mPeriod);
        return (mChartID);
    }

    void Close()
    {
        //
        if (mChartID != -1 && mChartID != 0)
        {
            ChartClose(mChartID);
            mChartID = 0;
        }
    }
    
    //
    // Properties ...

    /**
     * Get Chart ID ...
     *
     * @return ( ulong )
     */
    ulong ChartId()
    {
        return mChartID;
    }

    /**
     * Get Object Type ...
     *
     * @return ( int )
     **/
    int Type() const
    {
        return (0x1111);
    }

    /**
     * Get Chart Mode ...
     *
     * @return ( ENUM_CHART_MODE )
     **/
    ENUM_CHART_MODE Mode()
    {
        return ((ENUM_CHART_MODE)ChartGetInteger(mChartID, CHART_MODE));
    }

    /**
     * Get Chart Symbol ...
     *
     * @return ( string )
     */
    string Symbol()
    {
        return (ChartSymbol(mChartID));
    }

    /**
     * Get Chart Period ...
     *
     * @return ( ENUM_TIMEFRAMES )
     */
    ENUM_TIMEFRAMES Period()
    {
        return (ChartPeriod(mChartID));
    }

    /**
     * Count Chart Visible Bars ...
     *
     * @return ( int )
     */
    int VisibleBars()
    {
        return ((int)ChartGetInteger(mChartID, CHART_WIDTH_IN_BARS));
    }

    /**
     * Get Chart First Visible Bars Index ...
     *
     * @return ( int )
     */
    int FirstVisibleBar()
    {
        return ((int)ChartGetInteger(mChartID, CHART_FIRST_VISIBLE_BAR));
    }

    /**
     * Get Chart Last Visible Bars Index ...
     *
     * @return ( int )
     */
    int LastVisibleBar()
    {
        //
        int first = FirstVisibleBar();
        int count = VisibleBars();
        int result = first + count;
        return result;
    }

    /**
     * Get Chart's Specified SubWindows Minimum Price ...
     *
     * @param  subWindow: int
     * @return ( double )
     */
    double PriceMin(int subWindow = 0)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartGetDouble(mChartID, CHART_PRICE_MIN, subWindow));
    }

    /**
     * Get Chart's Specified SubWindows Maximum Price ...
     *
     * @param  subWindow: int
     * @return ( double )
     */
    double PriceMax(int subWindow = 0)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (ChartGetDouble(mChartID, CHART_PRICE_MAX, subWindow));
    }

    /**
     * Get Chart Width in Pixel ...
     *
     * @param  subWindow: int ...
     *
     * @return ( int )
     */
    int Width(int subWindow = 0)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (int)ChartGetInteger(mChartID, CHART_WIDTH_IN_PIXELS, subWindow);
    }

    /**
     * Get Chart Height in Pixel ...
     *
     * @param  subWindow: int ...
     *
     * @return ( int )
     */
    int Height(int subWindow = 0)
    {
        //
        // Normalize Subwindow ...
        if (subWindow <= 0)
        {
            subWindow = 0;
        }

        //
        return (int)ChartGetInteger(mChartID, CHART_HEIGHT_IN_PIXELS, subWindow);
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...

    //
    ulong mChartID;          // Chart ID ...
    string mSymbol;          // Chart Symbol ...
    ENUM_TIMEFRAMES mPeriod; // Chart Period ...
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-cobject.class.mq5

///////////////////////////////////////////////////////
//
// 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.x-pivot.analysis.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"
#include "../Libraries/x-saherelm.x-trade.lib.mq5"

//
#include <ChartObjects/ChartObject.mqh>
#include <ChartObjects/ChartObjectsArrows.mqh>
#include <ChartObjects/ChartObjectsFibo.mqh>
#include <ChartObjects/ChartObjectsLines.mqh>
#include <ChartObjects/ChartObjectsShapes.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>

//
// Definitions ...

enum ENUM_XCHARTOBJECTS
{
    //
    // Global ...
    X_EMPTY_OBJ = 7860,     // Empty
    X_BOX_OBJ = 7861,       // XBox
    X_BAR_ARROW_OBJ = 7862, // XBarArrow
    X_SIGNAL_OBJ = 8860,    // XSignal
    X_OHCL_OBJ = 8863,      // XBar
    X_RR_OBJ = 8864,        // XRR
    X_POSITION_OBJ = 8865,  // XPosition
    //
    // 9860 Custom Object ...
};

enum ENUM_X_CHART_IDS
{
    X_AT,        // AT
    X_TO,        // TO
    X_FROM,      // FROM
    X_UPPER,     // UPPER
    X_LOWER,     // LOWER
    X_ENTRY,     // ENTRY
    X_TARGET,    // TARGET
    X_SL,        // SL
    X_TP,        // TP
    X_BAR_H_SHW, // HSHW
    X_BAR_BDY,   // BODY
    X_BAR_L_SHW, // LSHW
};

string ToString(ENUM_X_CHART_IDS value)
{
    //
    string result = NULL;

    //
    result = EnumToString(value);

    //
    return result;
}

string ToXString(ENUM_XCHARTOBJECTS value)
{
    //
    return EnumToString(value);
}

struct XPOIStyle
{
    //
    // Properties ...
    int width;             // Width ...
    color clr;             // Color ...
    bool fill;             // Fill, Use in Rectangles ...
    ENUM_LINE_STYLE style; // Line Style ...

    //
    // Constructor ...
    XPOIStyle()
    {
        Default();
    }

    //
    // Tools ...

    //
    // Cleanup Model ...
    void Clean()
    {
        //
        width = 1;
        fill = false;
        clr = CLR_NONE;
        style = STYLE_SOLID;

        //
        ZeroMemory(this);
    }

    //
    // Default Props ...
    void Default()
    {
        //
        width = 1;
        fill = false;
        clr = CLR_NONE;
        style = STYLE_SOLID;
    }
};

//
// Extensions ...

/**
 * Converts a XBoxZone struct to XSignal instance ...
 *
 * @param  box: XBoxZone instance reference, provide source info ...
 * @param  signal: XSignal instance reference, holds prepared converted signal ...
 *
 * @return ( bool )
 */
bool ToXSignal(
    XBoxZone &box,
    XSignal &signal,
    double &rewardRatios[],
    double additionalSL = 0 //
)
{
    //
    bool result = false;

    //
    signal.Clean();

    //
    result = box.IsValid() &&
             HasChild(rewardRatios);
    if (!result)
    {
        return result;
    }

    //
    XOHCL entryBar;
    int entryIDX = box.ToIndex();
    if (entryIDX > 0)
    {
        entryIDX--;
    }
    result = entryBar.Init(
        box.symbol,
        box.period,
        entryIDX //
    );
    if (!result)
    {
        //
        entryBar.Clean();

        //
        return result;
    }

    //
    bool isBullish = box.IsBullish();
    bool isBearish = box.IsBearish();

    //
    ENUM_POSITION_TYPE iType =
        isBullish
            ? POSITION_TYPE_BUY
            : POSITION_TYPE_SELL;
    ENUM_X_POSITION_TYPES iXType =
        isBullish
            ? X_POSITION_TYPE_LONG
            : X_POSITION_TYPE_SHORT;

    //
    double points = GetPoints(box.symbol);

    //
    double entry =
        isBullish
            ? box.upper
            : box.lower;
    entry = entryBar.open;

    //
    double sl =
        isBullish
            ? box.lower
            : box.upper;
    if (additionalSL > 0)
    {
        //
        sl =
            isBullish
                ? sl - additionalSL
                : sl + additionalSL;
    }

    //
    double risk = MathAbs(entry - sl);

    //
    double tp = 0;

    //
    // Calculate Target ...
    XTarget targets[];

    //
    double tmp[];
    int idx = -1;
    XTarget iTarget;
    bool has = false;
    double iRatio = 0;
    double iReward = 0;
    double iRewardPrice = 0;
    Copy(
        rewardRatios,
        tmp //
    );
    while (HasChild(tmp))
    {
        //
        idx = -1;
        iRatio = GetMaxWidthIndex(
            tmp,
            idx //
        );

        //
        has = IsValidIndex(idx);
        if (!has)
        {
            break;
        }

        //
        has = iRatio > 0;

        //
        ArrayRemove(
            tmp,
            idx,
            1 //
        );

        //
        bool canSetTP = tp <= 0;

        //
        iReward = iRatio * risk;

        //
        iRewardPrice =
            isBullish
                ? entry + iReward
                : entry - iReward;
        if (canSetTP)
        {
            tp = iRewardPrice;
        }
        else
        {
            //
            iTarget.target = iRewardPrice;
            AddRef(
                iTarget,
                targets //
            );

            //
            iTarget.Clean();
        }
    }
    Clean(tmp);

    //
    // Filling Signal ...
    signal.tp = tp;
    signal.sl = sl;
    signal.type = iType;
    signal.comment = "";
    signal.entry = entry;
    signal.time = box.to;
    signal.volume = 0.01;
    signal.provider = box.type;
    signal.symbol = box.symbol;
    signal.period = box.period;
    signal.mode = X_ORDER_MODE_MARKET;

    //
    if (HasChild(targets))
    {
        //
        Copy(
            targets,
            signal.targets //
        );
    }

    //
    result = signal.IsValid();

    //
    // Cleanup Resources ...

    //
    if (!result)
    {
        signal.Clean();
    }

    //
    Clean(tmp);
    Clean(targets);
    iTarget.Clean();
    entryBar.Clean();

    //
    return result;
}

/**
 * Converts a Signal to Position ...
 *
 * @param  signal: XSignal, reference to Specified Signal ...
 * @param  position: XPosition, reference to Generated Position ...
 * @param  magic: ulong, Specified Magic Number ...
 * @param  ticket: ulong, Specified Position Ticket ...
 *
 * @return ( bool )
 */
bool ToXPosition(
    XSignal &signal,
    XPosition &position,
    ulong magic = 0,
    ulong ticket = 0 //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    position.Clean();

    //
    // Validate ...
    result = signal.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Normalize ...

    //
    if (ticket <= 0)
    {
        //
        if (signal.positionId > 0)
        {
            ticket = signal.positionId;
        }
        else
        {
            //
            ticket = MathRand();
            signal.positionId = ticket;
        }
    }
    signal.positionId = ticket;

    //
    if (magic <= 0)
    {
        magic = MathRand();
    }

    //
    // Filling Position ...
    position.swap = 0;
    position.profit = 0;
    position.magic = magic;
    position.commission = 0;
    position.tp = signal.tp;
    position.sl = signal.sl;
    position.ticket = ticket;
    position.type = signal.type;
    position.price = signal.entry;
    position.entry = signal.entry;
    position.openAt = signal.time;
    position.volume = signal.volume;
    position.symbol = signal.symbol;
    position.period = signal.period;
    position.comment = signal.comment;
    position.provider = signal.provider;

    //
    result = position.IsValid();

    //
    // Cleanup Resources ...
    if (!result)
    {
        position.Clean();
    }

    //
    return result;
}

/**
 * Update Specified Position by Bar ...
 *
 * @param  position: XPosition, reference to Specified Position ...
 * @param  barIndex: int, Specified Bar Index ...
 *
 * @return ( bool )
 */
bool XUpdatePosition(
    XPosition &position,
    int barIndex = 0 //
)
{
    //
    bool result = false;

    //
    // Normalize ...
    barIndex = NormalizeInt(barIndex, 0);

    //
    // Validate ...
    result = position.IsValid();
    if (!result)
    {
        return result;
    }

    //
    XOHCL bar;
    result = bar.Init(
        position.symbol,
        position.period,
        barIndex //
    );
    if (!result)
    {
        return result;
    }

    //
    double entryDiff = MathAbs(position.entry - position.price);
    bool isLong = IsLong(position.type);
    bool isInProfit = isLong
                          ? position.price > position.entry
                          : position.price < position.entry;
    bool isTargeted =
        isLong
            ? position.price >= position.tp
            : position.price <= position.tp;
    bool isStoped =
        isLong
            ? position.price <= position.sl
            : position.price >= position.sl;
    result = !isTargeted && !isStoped;
    if (!result)
    {
        return result;
    }

    //
    double spread = GetSpread(position.symbol);
    position.price = bar.close +
                     (isLong
                          ? spread
                          : (-1 * spread));
    entryDiff = MathAbs(position.entry - position.price);
    isInProfit = isLong
                     ? position.price > position.entry
                     : position.price < position.entry;
    result = position.IsValid();

    //
    // Cleanup Resources ...
    bar.Clean();

    //
    return result;
}

//
// Implementation ...

//
// Global Chart Objects ...

/**
 * Base Chart Object ...
 **/
class XCBaseObject : public CChartObject
{
    //
  public:
    //

    //
    // Getter(s) / Setter(s) ...

    /**
     * Get Object Specified Name ...
     *
     * @return ( string )
     */
    string ObjName()
    {
        return mObjName;
    }

    /**
     * Set Object Specified Name ...
     *
     * @param  value: String ...
     */
    void ObjName(string value)
    {
        //
        Name(value);
        mObjName = value;
    }

    virtual string TypeString()
    {
        return ToXString(X_EMPTY_OBJ);
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void) const
    {
        return ((int)X_EMPTY_OBJ);
    }

    /**
     * Destroy ...
     */
    void virtual Destroy()
    {
        //
        Detach();
        Delete();
    }

    //
  private:
    //

    //
    // Props ...
    string mObjName; // Object Specified Name ...

    //
};

/**
 * Bar Arrow Object ...
 **/
class XCBarArrowObject : public XCBaseObject
{
    //
  public:
    //

    /**
     * Creator ...
     *
     * @param  chart_id: Long, Specified Chart ID ...
     * @param  name: String, Specified Object ID ...
     * @param  window: Integer, Specified Chart Window ID ...
     * @param  swing: XCSwing instance ...
     *
     * @return ( bool )
     */
    bool CreateByBar(
        long chart_id,
        string name,
        int window,
        int arrow,
        XOHCL &bar,
        ENUM_X_PRICE priceType,
        double threshold = 5 //
    )
    {
        //
        bool result = false;

        //
        result =
            //
            bar.IsValid() &&
            IsValid(name)
            //
            ;
        if (!result)
        {
            return result;
        }

        //
        datetime time = bar.time;
        double price = bar.GetPrice(priceType);

        //
        if (threshold > 0)
        {
            //
            double thresholdPrice = threshold * GetPoints(bar.symbol);
            if (priceType == X_PRICE_HIGH)
            {
                price += thresholdPrice;
            }
            else if (priceType == X_PRICE_LOW)
            {
                price -= thresholdPrice;
            }
        }

        //
        result = mArrow.Create(
            chart_id,
            name,
            window,
            time,
            price,
            (char)arrow //
        );

        //
        if (result)
        {
            ObjName(name);
        }

        //
        return result;
    }

    //
    // Virtual ...

    /**
     * Destroy ...
     */
    void virtual Destroy()
    {
        //
        mArrow.Detach();
        mArrow.Delete();

        //
        Detach();
        Delete();
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void)
    {
        return (int)X_BAR_ARROW_OBJ;
    }

    /**
     * Type as String ...
     **/
    string TypeString() override
    {
        return ToXString(X_BAR_ARROW_OBJ);
    }

    //
    // Setter(s) ...

    /**
     * Set Arrow Anchor ...
     *
     * @param  value: ENUM_ARROW_ANCHOR member ...
     */
    void ArrowAnchor(ENUM_ARROW_ANCHOR value)
    {
        mArrow.Anchor(value);
    }

    /**
     * Set Arrow Color ...
     *
     * @param  value: Color ...
     */
    void ArrowColor(color value)
    {
        mArrow.Color(value);
    }

    /**
     * Set Arrow Width ...
     *
     * @param  value: Integer ...
     */
    void ArrowWidth(int value)
    {
        mArrow.Width(value);
    }

    //
  private:
    //
    CChartObjectArrow mArrow;

    //
};

/**
 * Signal Object ...
 **/
class XCSignalObject : public XCBaseObject
{
    //
  public:
    //

    //
    // Constructor ...
    XCSignalObject()
    {
    }

    //
    // Deconstructor ...
    ~XCSignalObject()
    {
    }

    /**
     * Create a Signal Object on Chart ...
     *
     * @param  chart_id: Long ...
     * @param  name: String ...
     * @param  window: Integer ...
     * @param  signal: XSignal instance ...
     * @param  length: int, Specified Length of Signal ...
     *
     * @return ( bool )
     */
    bool Create(
        long chart_id,
        const int window,
        XSignal &signal,
        int length = 3,
        string prefix = NULL //
    )
    {
        //
        bool result = false;

        //
        length = NormalizeInt(length, 3);

        //
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }

        //
        string name = signal.GetTag();
        if (IsValid(prefix))
        {
            name = prefix + "_" + name;
        }
        string nameMD5 = ToMD5(name);

        //
        int delay = (PeriodSeconds(signal.period) * length);
        datetime time1 = (datetime)((int)signal.time - delay);
        datetime time2 = (datetime)((int)signal.time + delay);

        //
        // TP ...
        string tpName = ToString(X_TP) + "_" + nameMD5;
        result = mTPTrend.Create(
            chart_id,
            tpName,
            window,
            time1,
            signal.tp,
            time2,
            signal.tp //
        );
        if (!result)
        {
            return result;
        }

        //
        // SL ...
        string slName = ToString(X_SL) + "_" + nameMD5;
        result = mSLTrend.Create(
            chart_id,
            slName,
            window,
            time1,
            signal.sl,
            time2,
            signal.sl //
        );
        if (!result)
        {
            //
            mTPTrend.Delete();
            return result;
        }

        //
        // TARGET ...
        bool hasTargets = HasChild(signal.targets);
        if (hasTargets)
        {
            //
            int targetsCount = ArraySize(signal.targets);
            for (int i = 0; i < targetsCount; i++)
            {
                //
                string targetName = ToString(X_TARGET) + "_" + ToXString(i) + "_" + nameMD5;
                CChartObjectTrend *mTargetTrend;
                mTargetTrend = new CChartObjectTrend();
                result = mTargetTrend.Create(
                    chart_id,
                    targetName,
                    window,
                    time1,
                    signal.targets[i].target,
                    time2,
                    signal.targets[i].target //
                );
                if (!result)
                {
                    //
                    mTPTrend.Delete();
                    mSLTrend.Delete();
                    break;
                }

                //
                mTargetTrends.Add(mTargetTrend);
            }
        }

        //
        // ENTRY ...
        string entrName = ToString(X_ENTRY) + "_" + nameMD5;
        result = mEntryTrend.Create(
            chart_id,
            entrName,
            window,
            time1,
            signal.entry,
            time2,
            signal.entry //
        );
        if (!result)
        {
            //
            mTPTrend.Delete();
            mSLTrend.Delete();
            mTargetTrends.Clear();
            return result;
        }

        //
        // Set Object Name ...
        ObjName(name);

        //
        return result;
    }

    //
    // Virtual ...

    /**
     * Destroy ...
     */
    void virtual Destroy()
    {
        //
        mTPTrend.Detach();
        mTPTrend.Delete();
        mSLTrend.Detach();
        mSLTrend.Delete();
        mEntryTrend.Detach();
        mEntryTrend.Delete();

        //
        mTargetTrends.Clear();

        //
        Detach();
        Delete();
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void)
    {
        return (int)X_SIGNAL_OBJ;
    }

    /**
     * Type as String ...
     **/
    string TypeString() override
    {
        return ToXString(X_SIGNAL_OBJ);
    }

    //
    // Setter(s) ...

    //
    // TP ...

    /**
     * Set TP Width ...
     *
     * @param  value: Integer ...
     */
    void TPWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mTPTrend.Width(value);
    }

    /**
     * Set TP Color ...
     *
     * @param  value: Color ...
     */
    void TPColor(color value)
    {
        mTPTrend.Color(value);
    }

    /**
     * Set TP Style ...
     *
     * @param  value: ENUM_LINE_STYLE ...
     */
    void TPStyle(ENUM_LINE_STYLE value)
    {
        mTPTrend.Style(value);
    }

    //
    // SL ...

    /**
     * Set SL Width ...
     *
     * @param  value: Integer ...
     */
    void SLWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mSLTrend.Width(value);
    }

    /**
     * Set SL Color ...
     *
     * @param  value: Color ...
     */
    void SLColor(color value)
    {
        mSLTrend.Color(value);
    }

    /**
     * Set SL Style ...
     *
     * @param  value: ENUM_LINE_STYLE ...
     */
    void SLStyle(ENUM_LINE_STYLE value)
    {
        mSLTrend.Style(value);
    }

    //
    // TARGET ...

    /**
     * Set Target Width ...
     *
     * @param  value: Integer ...
     */
    void TargetWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        for (int i = 0; i < mTargetTrends.Total(); i++)
        {
            //
            CChartObjectTrend *iTrend = (CChartObjectTrend *)mTargetTrends.At(i);
            iTrend.Width(value);
        }
    }

    /**
     * Set Target Color ...
     *
     * @param  value: Color ...
     */
    void TargetColor(color value)
    {
        //
        for (int i = 0; i < mTargetTrends.Total(); i++)
        {
            //
            CChartObjectTrend *iTrend = (CChartObjectTrend *)mTargetTrends.At(i);
            iTrend.Color(value);
        }
    }

    /**
     * Set Target Style ...
     *
     * @param  value: ENUM_LINE_STYLE ...
     */
    void TargetStyle(ENUM_LINE_STYLE value)
    {
        //
        for (int i = 0; i < mTargetTrends.Total(); i++)
        {
            //
            CChartObjectTrend *iTrend = (CChartObjectTrend *)mTargetTrends.At(i);
            iTrend.Style(value);
        }
    }

    //
    // ENTRY ...

    /**
     * Set Entry Width ...
     *
     * @param  value: Integer ...
     */
    void EntryWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mEntryTrend.Width(value);
    }

    /**
     * Set Entry Color ...
     *
     * @param  value: Color ...
     */
    void EntryColor(color value)
    {
        mEntryTrend.Color(value);
    }

    /**
     * Set Entry Style ...
     *
     * @param  value: ENUM_LINE_STYLE ...
     */
    void EntryStyle(ENUM_LINE_STYLE value)
    {
        mEntryTrend.Style(value);
    }

    //
  private:
    //

    //
    CChartObjectTrend mTPTrend;
    CChartObjectTrend mSLTrend;
    CChartObjectTrend mEntryTrend;
    CArrayObj mTargetTrends;
};

/**
 * XRR Object ...
 **/
class XCRRObject : public XCSignalObject
{
    //
    // Public ...
  public:
    //
    // Constructor ...
    bool CreateBoxRR(
        long chart_id,
        const int window,
        XBoxZone &box,
        double &rewardRatios[],
        int length = 3,
        string prefix = NULL //
    )
    {
        //
        bool result = false;

        //
        length = NormalizeInt(length, 3);

        //
        result = box.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XSignal signal;
        result = ToXSignal(
            box,
            signal,
            rewardRatios //
        );
        if (!result)
        {
            //
            signal.Clean();

            //
            return result;
        }

        //
        result = Create(
            chart_id,
            window,
            signal,
            length,
            prefix //
        );

        //
        signal.Clean();

        //
        return result;
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void)
    {
        return (int)X_RR_OBJ;
    }

    /**
     * Type as String ...
     **/
    string TypeString() override
    {
        return ToXString(X_RR_OBJ);
    }
};

/**
 * XOHCL Object ...
 **/
class XCOHCLObject : public XCBaseObject
{
    //
  public:
    //

    /**
     * Creator ...
     *
     * @param  chart_id: Long, Specified Chart ID ...
     * @param  window: Integer, Specified Chart Window ID ...
     * @param  pivot: XPVPivot instance ...
     * @param  prefix: String ...
     *
     * @return ( bool )
     */
    bool Create(
        long chart_id,
        int window,
        XOHCL &bar,
        datetime to,
        string prefix = NULL //
    )
    {
        //
        bool result = false;

        //
        result = bar.IsValid() &&
                 IsValid(to);
        if (!result)
        {
            return result;
        }

        //
        string name = bar.GetTag();
        if (IsValid(prefix))
        {
            name = prefix + "_" + name;
        }
        string nameMD5 = ToMD5(name);

        //
        // High Shadow ...
        string hShadowName = ToString(X_BAR_H_SHW) + "_" + nameMD5;

        //
        result = mHighShadowRect.Create(
            chart_id,
            hShadowName,
            window,
            bar.time,
            bar.high,
            to,
            bar.GetUp() //
        );
        if (!result)
        {
            return result;
        }

        //
        // Body ...
        string bodyName = ToString(X_BAR_BDY) + "_" + nameMD5;

        //
        result = mBodyRect.Create(
            chart_id,
            bodyName,
            window,
            bar.time,
            bar.GetUp(),
            to,
            bar.GetDown() //
        );
        if (!result)
        {
            //
            mHighShadowRect.Delete();
            mHighShadowRect.Detach();
            return result;
        }

        //
        // Low Shadow ...
        string lShadowName = ToString(X_BAR_L_SHW) + "_" + nameMD5;

        //
        result = mLowShadowRect.Create(
            chart_id,
            lShadowName,
            window,
            bar.time,
            bar.GetDown(),
            to,
            bar.low //
        );
        if (!result)
        {
            //
            mBodyRect.Delete();
            mBodyRect.Detach();
            mHighShadowRect.Delete();
            mHighShadowRect.Detach();
            return result;
        }

        //
        if (result)
        {
            ObjName(name);
        }

        //
        return result;
    }

    //
    // Virtual ...

    /**
     * Destroy ...
     */
    void virtual Destroy()
    {
        //
        mBodyRect.Detach();
        mBodyRect.Delete();

        //
        mLowShadowRect.Detach();
        mLowShadowRect.Delete();

        //
        mHighShadowRect.Detach();
        mHighShadowRect.Delete();

        //
        Detach();
        Delete();
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void)
    {
        return (int)X_OHCL_OBJ;
    }

    /**
     * Type as String ...
     **/
    string TypeString() override
    {
        return ToXString(X_OHCL_OBJ);
    }

    //
    // Getter(s) / Setter(s) ...

    /**
     * Set High Shadow Width ...
     *
     * @param  value: Integer ...
     */
    void HighShadowWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mHighShadowRect.Width(value);
    }

    /**
     * Set High Shadow Color ...
     *
     * @param  value: Color ...
     */
    void HighShadowColor(color value)
    {
        mHighShadowRect.Color(value);
    }

    /**
     * Set High Shadow Style ...
     *
     * @param  value: ENUM_LINE_STYLE member ...
     */
    void HighShadowStyle(ENUM_LINE_STYLE value)
    {
        mHighShadowRect.Style(value);
    }

    /**
     * Set High Shadow Fill ...
     *
     * @param  value: Boolean ...
     */
    void HighShadowFill(bool value)
    {
        mHighShadowRect.Fill(value);
    }

    /**
     * Set Body Width ...
     *
     * @param  value: Integer ...
     */
    void BodyWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mBodyRect.Width(value);
    }

    /**
     * Set Body Color ...
     *
     * @param  value: Color ...
     */
    void BodyColor(color value)
    {
        mBodyRect.Color(value);
    }

    /**
     * Set Body Style ...
     *
     * @param  value: ENUM_LINE_STYLE member ...
     */
    void BodyStyle(ENUM_LINE_STYLE value)
    {
        mBodyRect.Style(value);
    }

    /**
     * Set Body Fill ...
     *
     * @param  value: Boolean ...
     */
    void BodyFill(bool value)
    {
        mBodyRect.Fill(value);
    }

    /**
     * Set Low Shadow Width ...
     *
     * @param  value: Integer ...
     */
    void LowShadowWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mLowShadowRect.Width(value);
    }

    /**
     * Set Low Shadow Color ...
     *
     * @param  value: Color ...
     */
    void LowShadowColor(color value)
    {
        mLowShadowRect.Color(value);
    }

    /**
     * Set Low Shadow Style ...
     *
     * @param  value: ENUM_LINE_STYLE member ...
     */
    void LowShadowStyle(ENUM_LINE_STYLE value)
    {
        mLowShadowRect.Style(value);
    }

    /**
     * Set Low Shadow Fill ...
     *
     * @param  value: Boolean ...
     */
    void LowShadowFill(bool value)
    {
        mLowShadowRect.Fill(value);
    }

    void BarSpes(
        int width = 1,
        color clr = clrNONE,
        ENUM_LINE_STYLE style = STYLE_SOLID,
        bool fill = false //
    )
    {
        //
        BodyWidth(width);
        LowShadowWidth(width);
        HighShadowWidth(width);

        //
        BodyColor(clr);
        LowShadowColor(clr);
        HighShadowColor(clr);

        //
        BodyStyle(style);
        LowShadowStyle(style);
        HighShadowStyle(style);

        //
        BodyFill(fill);
        LowShadowFill(fill);
        HighShadowFill(fill);
    }

    //
  private:
    //

    //
    // Props ...
    CChartObjectRectangle mHighShadowRect;
    CChartObjectRectangle mBodyRect;
    CChartObjectRectangle mLowShadowRect;
};

/**
 * Box Object ...
 **/
class XCBoxObject : public XCBaseObject
{
    //
  public:
    //

    /**
     * Creator ...
     *
     * @param  chart_id: Long, Specified Chart ID ...
     * @param  window: Integer, Specified Chart Window ID ...
     * @param  pivot: XPVPivot instance ...
     * @param  prefix: String ...
     *
     * @return ( bool )
     */
    bool Create(
        long chart_id,
        int window,
        string name,
        double upper,
        double lower,
        datetime from,
        datetime to,
        datetime at = NULL,
        string prefix = NULL //
    )
    {
        //
        bool result = false;

        //
        result =
            upper > 0 &&
            lower > 0 &&
            to > from &&
            IsValid(to) &&
            IsValid(name) &&
            IsValid(from) &&
            upper > lower;
        if (!result)
        {
            return result;
        }

        //
        if (IsValid(prefix))
        {
            name = prefix + "_" + name;
        }
        string nameMD5 = ToMD5(name);

        //
        result = mRect.Create(
            chart_id,
            name,
            window,
            from,
            upper,
            to,
            lower //
        );
        if (!result)
        {
            return result;
        }

        //
        if (result)
        {
            //
            if (IsValid(at))
            {
                //
                result = mAt.Create(
                    chart_id,
                    name + ToString(X_AT),
                    window,
                    at //
                );
            }

            //
            if (!result)
            {
                //
                mAt.Detach();
                mAt.Delete();

                //
                mRect.Detach();
                mRect.Delete();
            }
        }

        //
        if (result)
        {
            ObjName(name);
        }

        //
        return result;
    }

    //
    // Virtual ...

    /**
     * Destroy ...
     */
    void virtual Destroy()
    {
        //
        mAt.Detach();
        mAt.Delete();

        //
        mRect.Detach();
        mRect.Delete();

        //
        Detach();
        Delete();
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    virtual int Type(void)
    {
        return (int)X_BOX_OBJ;
    }

    /**
     * Type as String ...
     **/
    string TypeString() override
    {
        return ToXString(X_BOX_OBJ);
    }

    //
    // Getter(s) / Setter(s) ...

    /**
     * Set Width ...
     *
     * @param  value: Integer ...
     */
    void BoxWidth(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mAt.Width(value);
        mRect.Width(value);
    }

    /**
     * Set Color ...
     *
     * @param  value: Color ...
     */
    void BoxColor(color value)
    {
        //
        mAt.Color(value);
        mRect.Color(value);
    }

    /**
     * Set Style ...
     *
     * @param  value: ENUM_LINE_STYLE member ...
     */
    void BoxStyle(ENUM_LINE_STYLE value)
    {
        //
        mAt.Style(value);
        mRect.Style(value);
    }

    /**
     * Set Fill ...
     *
     * @param  value: Boolean ...
     */
    void BoxFill(bool value)
    {
        mRect.Fill(value);
    }

    //
  private:
    //

    //
    // Props ...
    CChartObjectVLine mAt;
    CChartObjectRectangle mRect;
};

/**
 * Position Object ...
 **/
class XCPositionObject : public XCBaseObject
{
    //
    // Public ...
  public:
    //
    XPOIStyle riskStyle;        // Risk Box Style ...
    color inProfitColor;        // In Profit Price Box Color ...
    color inDrawdownColor;      // In Drawdown Price Box Color ...
    XPOIStyle rewardStyle;      // Reward Box Style ...
    ENUM_LINE_STYLE priceStyle; // Price Box Style ...

    //
    // Creators ...

    /**
     * Create a Position Object by Signal reference ...
     *
     * @param  chart_id: Long, Specified Chart ID ...
     * @param  window: Integer, Specified Chart Window ID ...
     * @param  signal: XSignal, reference to Provided Signal ...
     * @param  magic: ulong, Specified Position Magic Number ...
     * @param  ticket: ulong, Specified Position Ticket ...
     *
     * @return ( bool )
     */
    bool Create(
        long chart_id,
        int window,
        XSignal &signal,
        ulong magic = 0,
        ulong ticket = 0 //
    )
    {
        //
        bool result = false;

        //
        // Converts Signal to Test Position ...
        XPosition _position;
        result = ToXPosition(
            signal,
            _position,
            magic,
            ticket //
        );

        //
        // Validate ...
        result = chart_id >= 0 &&
                 window >= 0 &&
                 _position.IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = Create(
            chart_id,
            window,
            _position //
        );

        //
        return result;
    }

    /**
     * Create a Position Object ...
     *
     * @param  chart_id: Long, Specified Chart ID ...
     * @param  window: Integer, Specified Chart Window ID ...
     * @param  position: XPosition, reference to Specified Position ...
     *
     * @return ( bool )
     */
    bool Create(
        long chart_id,
        int window,
        XPosition &position //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result = chart_id >= 0 &&
                 window >= 0 &&
                 position.IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = Update(position);
        if (!result)
        {
            //
            Destroy();
            return result;
        }

        //
        return result;
    }

    /**
     * Update Position if Belongs to ...
     *
     * @param  position: XPosition, reference to Specified Position ...
     * @param  barIndex: int, Specified Bar Index ...
     *
     * @return ( bool )
     */
    bool Update(
        XPosition &position,
        int barIndex = 0 //
    )
    {
        //
        bool result = false;

        //
        // Normalize ...
        barIndex = NormalizeInt(barIndex, 0);

        //
        // Validate ...
        result =
            mChartID >= 0 &&
            mWindowID >= 0 &&
            position.IsValid() &&
            (!HasPosition()
                 ? true
                 : IsBelong(position));
        if (!result)
        {
            return result;
        }

        //
        datetime _toTime = GetBarTime(
            position.symbol,
            position.period,
            barIndex //
        );

        //
        // Validate Chart Info ...
        result = UpdatePosition(
            position,
            _toTime //
        );

        //
        return result;
    }

    /**
     * Update Position if Belongs to ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     *
     * @return ( bool )
     */
    bool Update(int barIndex = 0)
    {
        //
        bool result = false;

        //
        // Normalize ...
        barIndex = NormalizeInt(barIndex, 0);

        //
        // Validate ...
        result = HasPosition();
        if (!result)
        {
            return result;
        }

        //
        // Update mPosition ...
        result = XUpdatePosition(mPosition, barIndex);
        if (!result)
        {
            return result;
        }

        //
        result = Update(
            mPosition,
            barIndex //
        );

        //
        return result;
    }

    //
    // Retrievers ...

    bool GetPosition(XPosition &_position)
    {
        //
        bool result = false;

        //
        // Prepare ...
        _position.Clean();

        //
        // Validate ...
        result = HasPosition();
        if (!result)
        {
            return result;
        }

        //
        _position = mPosition;

        //
        return result;
    }

    //
    // Checkers ...

    /**
     * Check Object is Belongs to Specified Position or not ...
     *
     * @param  position: XPosition, reference to Specified Position ...
     *
     * @return ( bool )
     */
    bool IsBelong(XPosition &position)
    {
        //
        bool result = false;

        //
        // Validate ...
        result =
            HasPosition() &&
            position.IsValid();

        //
        // Check ...
        result =
            result &&
            mPosition.ticket == position.ticket;

        //
        return result;
    }

    //
    // Overrides ...

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    string TypeString() override
    {
        return ToXString(X_POSITION_OBJ);
    }

    /**
     * Retrieve Object Type ...
     *
     * @return ( ENUM_XCHARTOBJECTS )
     */
    int Type(void) const override
    {
        return ((int)X_POSITION_OBJ);
    }

    /**
     * Destroy Object ...
     **/
    void Destroy() override
    {
        //
        ObjName(NULL);

        //
        mRiskBox.Clean();
        mPosition.Clean();
        mPriceBox.Clean();
        mRewardBox.Clean();

        //
        mRiskBoxObj.Detach();
        mRiskBoxObj.Delete();

        //
        mPriceBoxObj.Detach();
        mPriceBoxObj.Delete();

        //
        mRewardBoxObj.Detach();
        mRewardBoxObj.Delete();
    }

    //
    // Stylers ...

    /**
     * Apply Styles based on Current Style Configurations ...
     */
    void ApplyStyle()
    {
        //
        // Validate ...
        bool has = HasPosition();
        if (!has)
        {
            return;
        }

        //
        // Check Position in Profit ...
        bool isLong = IsLong(mPosition.type);
        bool isRiskFreed =
            isLong
                ? mPosition.sl > mPosition.entry
                : mPosition.sl < mPosition.entry;
        bool isInProfit = mPosition.profit > 0;
        XPOIStyle _priceStyle;
        if (isInProfit)
        {
            //
            _priceStyle = rewardStyle;
            _priceStyle.clr = inProfitColor;
        }
        else
        {
            //
            _priceStyle = riskStyle;
            _priceStyle.clr = inDrawdownColor;
        }

        //
        // Apply Profit Based Styles ...
        _priceStyle.fill = true;
        _priceStyle.style = priceStyle;

        //
        // Change Risk Style if Risk Freed Position ...
        if (isRiskFreed)
        {
            //
            riskStyle.fill = true;
            riskStyle.clr = rewardStyle.clr;
        }

        //
        // Risk Style ...
        mRiskBoxObj.BoxColor(riskStyle.clr);
        mRiskBoxObj.BoxFill(riskStyle.fill);
        mRiskBoxObj.BoxStyle(riskStyle.style);
        mRiskBoxObj.BoxWidth(riskStyle.width);

        //
        // Reward Style ...
        mRewardBoxObj.BoxColor(rewardStyle.clr);
        mRewardBoxObj.BoxFill(rewardStyle.fill);
        mRewardBoxObj.BoxStyle(rewardStyle.style);
        mRewardBoxObj.BoxWidth(rewardStyle.width);

        //
        // Price Style ..
        mPriceBoxObj.BoxColor(_priceStyle.clr);
        mPriceBoxObj.BoxFill(_priceStyle.fill);
        mPriceBoxObj.BoxStyle(_priceStyle.style);
        mPriceBoxObj.BoxWidth(_priceStyle.width);

        //
        // Cleanup Resources ...
        _priceStyle.Clean();
    }

    //
    void ApplyStyle(
        XPOIStyle &bullishStyle,
        XPOIStyle &bearishStyle //
    )
    {
        //
        // Validate ...
        if (!HasPosition())
        {
            return;
        }

        //
        // Risk Box ...
        if (mRiskBox.IsValid())
        {
            //
            mRiskBoxObj.BoxColor(bearishStyle.clr);
            mRiskBoxObj.BoxFill(bearishStyle.fill);
            mRiskBoxObj.BoxStyle(bearishStyle.style);
            mRiskBoxObj.BoxWidth(bearishStyle.width);
        }

        //
        // Reward Box ...
        if (mRewardBox.IsValid())
        {
            //
            mRewardBoxObj.BoxColor(bullishStyle.clr);
            mRewardBoxObj.BoxFill(bullishStyle.fill);
            mRewardBoxObj.BoxStyle(bullishStyle.style);
            mRewardBoxObj.BoxWidth(bullishStyle.width);
        }

        //
        // Price Box ...
        if (mPriceBox.IsValid())
        {
            //
            XPOIStyle _priceStyle;
            if (mPriceBox.IsBullish())
            {
                _priceStyle = bullishStyle;
            }
            else
            {
                _priceStyle = bearishStyle;
            }

            //
            mPriceBoxObj.BoxColor(_priceStyle.clr);
            mPriceBoxObj.BoxFill(_priceStyle.fill);
            mPriceBoxObj.BoxStyle(_priceStyle.style);
            mPriceBoxObj.BoxWidth(_priceStyle.width);

            //
            _priceStyle.Clean();
        }
    }

    //
    // Protected ...
  protected:
    //

    //
    bool HasPosition()
    {
        return mPosition.IsValid();
    }

    //
    bool UpdatePosition(
        XPosition &_position,
        datetime _toTime = NULL //
    )
    {
        //
        bool result = false;

        //
        // Normalize ...
        _toTime = NormalizeTime(_toTime);

        //
        // Validate ...
        result = _position.IsValid() &&
                 (!HasPosition()
                      ? true
                      : _position.ticket == mPosition.ticket //
                 );
        if (!result)
        {
            return result;
        }

        //
        mPosition = _position;
        result = UpdateZones(_toTime);

        //
        return result;
    }

    //
    bool UpdateZones(datetime _toTime = NULL)
    {
        //
        bool result = false;

        //
        // Normalize ...
        _toTime = NormalizeTime(_toTime);

        //
        // Validate ...
        result = HasPosition();
        if (!result)
        {
            return result;
        }

        //
        string iSymbol = mPosition.symbol;
        ENUM_TIMEFRAMES iPeriod = mPosition.period;

        //
        datetime iAt = mPosition.openAt;
        datetime iFrom = mPosition.openAt;

        //
        datetime _defaultToTime = iFrom + (PeriodSeconds(iPeriod) * 3);
        datetime iTo = _toTime <= _defaultToTime
                           ? _defaultToTime
                           : _toTime;

        //
        double iTP = mPosition.tp;
        double iSL = mPosition.sl;
        double iEntry = mPosition.entry;
        double iPrice = mPosition.price;

        //
        bool isLong = IsLong(mPosition.type);
        ENUM_X_DIRECTION iRiskDir = isLong
                                        ? X_DIRECTION_BEARISH
                                        : X_DIRECTION_BULLISH;
        ENUM_X_DIRECTION iRewardDir = isLong
                                          ? X_DIRECTION_BULLISH
                                          : X_DIRECTION_BEARISH;

        //
        bool isInProfit = mPosition.profit > 0;
        bool isRiskFreed =
            isLong
                ? mPosition.sl > mPosition.entry
                : mPosition.sl < mPosition.entry;
        ENUM_X_DIRECTION iPriceDir = isInProfit
                                         ? X_DIRECTION_BULLISH
                                         : X_DIRECTION_BEARISH;

        //
        // Try to Update Zones ...
        string iSuffix = ToXString(mPosition.type) + "_" + ToXString(mPosition.ticket);
        string iPrefix = ToXString(TimeToSeconds(mPosition.openAt));
        string iName = iSuffix + "_" + iPrefix;
        string iRiskName = iSuffix + "_" + "RISK" + "_" + iPrefix;
        string iPriceName = iSuffix + "_" + "PRICE" + "_" + iPrefix;
        string iRewardName = iSuffix + "_" + "REWARD" + "_" + iPrefix;

        //
        // Risk Box ...
        mRiskBox.to = iTo;
        mRiskBox.at = iAt;
        mRiskBox.lower =
            isLong
                ? isRiskFreed
                      ? iEntry
                      : iSL
            : isRiskFreed
                ? iSL
                : iEntry;
        mRiskBox.upper =
            isLong
                ? isRiskFreed
                      ? iSL
                      : iEntry
            : isRiskFreed
                ? iEntry
                : iSL;
        mRiskBox.from = iFrom;
        mRiskBox.symbol = iSymbol;
        mRiskBox.period = iPeriod;
        mRiskBox.type = iRiskName;
        mRiskBox.dir = isRiskFreed
                           ? X_DIRECTION_BEARISH
                           : X_DIRECTION_BULLISH;

        //
        // Reward Box ...
        mRewardBox.to = iTo;
        mRewardBox.at = iAt;
        mRewardBox.lower =
            isLong
                ? iEntry
                : iTP;
        mRewardBox.upper =
            isLong
                ? iTP
                : iEntry;
        mRewardBox.from = iFrom;
        mRewardBox.symbol = iSymbol;
        mRewardBox.period = iPeriod;
        mRewardBox.type = iRewardName;
        mRewardBox.dir = X_DIRECTION_BULLISH;

        //
        // Price Box ...
        mPriceBox.to = iTo;
        mPriceBox.at = iAt;
        mPriceBox.lower =
            isLong
                ? isInProfit
                      ? isRiskFreed
                            ? iSL
                            : iEntry
                      : iPrice
            : isInProfit
                ? isRiskFreed
                      ? iSL
                      : iPrice
                : iEntry;
        mPriceBox.upper =
            isLong
                ? isInProfit
                      ? iPrice
                      : iEntry
            : isInProfit
                ? iEntry
                : iPrice;
        mPriceBox.from = iFrom;
        mPriceBox.symbol = iSymbol;
        mPriceBox.period = iPeriod;
        mPriceBox.type = iPriceName;
        mPriceBox.dir = isInProfit
                            ? X_DIRECTION_BULLISH
                            : X_DIRECTION_BEARISH;

        //
        result = mRiskBox.IsValid() &&
                 mPriceBox.IsValid() &&
                 mRewardBox.IsValid();
        if (!result)
        {
            return result;
        }

        //
        mRiskBoxObj.Detach();
        mRiskBoxObj.Delete();
        bool isRiskCreated =
            mRiskBoxObj.Create(
                mChartID,
                mWindowID,
                mRiskBox.type,
                mRiskBox.upper,
                mRiskBox.lower,
                mRiskBox.from,
                mRiskBox.to,
                NULL, // At ...
                NULL  // Prefix ...
            );

        //
        mRewardBoxObj.Detach();
        mRewardBoxObj.Delete();
        bool isRewardCreated =
            mRewardBoxObj.Create(
                mChartID,
                mWindowID,
                mRewardBox.type,
                mRewardBox.upper,
                mRewardBox.lower,
                mRewardBox.from,
                mRewardBox.to,
                NULL, // At ...
                NULL  // Prefix ...
            );

        //
        mPriceBoxObj.Detach();
        mPriceBoxObj.Delete();
        bool isPriceCreated =
            mPriceBoxObj.Create(
                mChartID,
                mWindowID,
                mPriceBox.type,
                mPriceBox.upper,
                mPriceBox.lower,
                mPriceBox.from,
                mPriceBox.to,
                NULL, // At ...
                NULL  // Prefix ...
            );

        //
        result =
            isRiskCreated &&
            isPriceCreated &&
            isRewardCreated;
        if (result)
        {
            //
            ObjName(iName);

            //
            // Apply Styles ...
            ApplyStyle();
        }

        //
        return result;
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    int mWindowID;
    long mChartID;

    //
    XBoxZone mRiskBox;
    XBoxZone mPriceBox;
    XPosition mPosition;
    XBoxZone mRewardBox;

    //
    XCBoxObject mRiskBoxObj;
    XCBoxObject mPriceBoxObj;
    XCBoxObject mRewardBoxObj;

    //
};

//
// Extensions ...

/**
 * Add all Items of Specified CArrayObj into another ...
 *
 * @param  source: CArrayObj, pointer refrence Specified Data Source ...
 * @param  dest: CArrayObj, pointer refrence Specified Destination of Adding ...
 */
void AddObjects(
    CArrayObj *&source,
    CArrayObj &dest //
)
{
    //
    bool has = source != NULL &&
               source.Total() > 0;
    if (!has)
    {
        return;
    }

    //
    for (int i = 0; i < source.Total(); i++)
    {
        dest.Add(source.At(i));
    }
}

/**
 * Remove Drawn Objects from Chart ...
 *
 * @param  tag: Specified Object Name, if NULL remove all Objects ...
 * @param  chartId: long, Specified Chart ...
 * @param  subWindow: int Specified Sub Window ...
 */
void RemoveObjects(
    string tag = NULL,
    long chartId = -1,
    int subWindow = -1 //
)
{
    //
    // Normalize Args ...

    //
    if (chartId == -1)
    {
        chartId = 0;
    }

    //
    if (subWindow == -1)
    {
        subWindow = 0;
    }

    //
    bool has = false;
    string iName = NULL;
    for (int iObj = ObjectsTotal(chartId, subWindow) - 1; iObj >= 0; iObj--)
    {
        //
        // Retrieve Object Name ...
        iName = ObjectName(chartId, iObj, subWindow);

        //
        // Check Can Delete Object or not ...
        has =
            !IsSpecifiedValid(tag)
                ? true
                : StringFind(iName, tag) >= 0;
        if (has)
        {
            ObjectDelete(chartId, iName);
        }
    }

    //
    // Redraw Chart ...
    ChartRedraw();
}

//
// Color Manipulation Functions ...

/**
 * Apply Alph to Color ...
 *
 * @param  _clr: Specified Color ...
 * @param  _alpha: Specified Alpha to Apply ...
 *
 * @return ( color )
 */
color ApplyAlpha(
    color _clr,
    uchar _alpha = 255 //
)
{
    //
    color result = _clr;

    //
    // Normalize ...
    _alpha = (uchar)NormalizeInt(_alpha, 0, 255);

    //
    uchar alpha = _alpha / 255;

    //
    uint uintColor = ColorToARGB(_clr, alpha);
    result = (color)uintColor;

    //
    return result;
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-data.collector.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Indicator
// -------------------------------------------------
// Name: XCDataCollector ...
// Description: Class for Handling Data Read or Write ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm Class for Handling Data Read or Write"
#property strict

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"

//
// Definitions ...

//
// Implementation ...
class XCDataCollector : public XCBase
{
    //
    // Public ...
  public:
    //
    // Constructors ...
    XCDataCollector()
    {
        mPath = "XDataCollector";
    }

    //
    // Deconstructor ...
    ~XCDataCollector()
    {
    }

    //
    // Properties ...

    //
    string Path()
    {
        return mPath;
    }

    //
    void Path(string value)
    {
        mPath = value;
    }

    //
    // Tools ...

    //
    string GetFilePath(string fileName)
    {
        //
        string result = "";

        //
        result =
            //
            (IsValid(mPath) ? mPath + "\\" : "") + fileName + ".x121.log"
            //
            ;

        //
        return result;
    }

    //
    int GetFileHandlerForWrite(string filePath)
    {
        //
        int result = INVALID_HANDLE;

        //
        if (!IsValid(filePath))
        {
            return result;
        }

        //
        result = FileOpen(
            filePath,
            FILE_READ | FILE_WRITE | FILE_TXT //
        );

        //
        return result;
    }

    //
    int GetFileHandlerForRead(string filePath)
    {
        //
        int result = INVALID_HANDLE;

        //
        if (!IsValid(filePath))
        {
            return result;
        }

        //
        result = FileOpen(
            filePath,
            FILE_READ | FILE_TXT | FILE_ANSI //
        );

        //
        return result;
    }

    /**
     * Save Specified Content into Specified File Name ...
     *
     * @param  fileName: string, file name ...
     * @param  content: string, content ...
     *
     * @return ( bool )
     */
    bool Save(
        string fileName,
        string content //
    )
    {
        //
        bool result = false;

        //
        result = IsValid(fileName) &&
                 IsValid(content);
        if (!result)
        {
            return result;
        }

        //
        int mHandler = GetFileHandlerForWrite(fileName);
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        FileWrite(mHandler, content);
        FileFlush(mHandler);
        FileClose(mHandler);

        //
        return result;
    }

    /**
     * Append Specified Content into Specified File Name ...
     *
     * @param  fileName: string, file name ...
     * @param  content: string, content ...
     *
     * @return ( bool )
     */
    bool Append(
        string fileName,
        string content //
    )
    {
        //
        bool result = false;

        //
        result = IsValid(fileName) &&
                 IsValid(content);
        if (!result)
        {
            return result;
        }

        //
        int mHandler = GetFileHandlerForWrite(fileName);
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        FileSeek(mHandler, 0, SEEK_END);
        FileWrite(mHandler, content);
        FileFlush(mHandler);
        FileClose(mHandler);

        //
        return result;
    }

    /**
     * Read Specified File Content ...
     *
     * @param  fileName: string, file name ...
     * @param  content: string reference, hold's reading content ...
     *
     * @return ( bool )
     */
    bool Read(
        string fileName,
        string &content //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        content = NULL;

        //
        // Validate Args ...
        result = IsValid(fileName);
        if (!result)
        {
            return result;
        }

        //
        int mHandler = GetFileHandlerForRead(fileName);
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        // Reading File ...
        while (!FileIsEnding(mHandler))
        {
            //
            string iLine = FileReadString(mHandler);
            content += iLine;
        }

        //
        // Close File ...
        FileClose(mHandler);

        //
        return result;
    }

    /**
     * Read Specified File Content ...
     *
     * @param  fileName: string, file name ...
     * @param  content: string reference collection, hold's reading content lines ...
     *
     * @return ( bool )
     */
    bool Read(
        string fileName,
        string &content[] //
    )
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        Clean(content);

        //
        // Validate Args ...
        result = IsValid(fileName);
        if (!result)
        {
            return result;
        }

        //
        int mHandler = GetFileHandlerForRead(fileName);
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        // Reading File ...
        while (!FileIsEnding(mHandler))
        {
            //
            string iLine = FileReadString(mHandler);
            Add(
                iLine,
                content //
            );
        }

        //
        // Close File ...
        FileClose(mHandler);

        //
        return result;
    }

    /**
     * Check Specified File Exists or not ...
     *
     * @param  fileName: string, Full Path of File to Check ...
     *
     * @return ( bool )
     */
    bool IsExists(string fileName)
    {
        //
        bool result = false;

        //
        // Validate Args ...
        result = IsValid(fileName);
        if (!result)
        {
            return result;
        }

        //
        // Check File Exists or not ...
        int mHandler = GetFileHandlerForRead(fileName);
        result = mHandler != INVALID_HANDLE;
        FileClose(mHandler);

        //
        return result;
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...

    //
    string mPath; // Base Collector Path ...
};


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-expert.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class
// -------------------------------------------------
// Name: XCBaseExpret ...
// Description: Base Expert Class ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm Base Expert Class"
#property strict

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Classes/x-saherelm.x-chart.helper.class.mq5"
#include "../Classes/x-saherelm.x-guard.class.mq5"
#include "../Classes/x-saherelm.x-position.drawer.class.mq5"
#include "../Classes/x-saherelm.x-signaller.class.mq5"
#include "../Classes/x-saherelm.x-target.class.mq5"
#include "../Classes/x-saherelm.x-trade-manager.class.mq5"
#include "../Classes/x-saherelm.x-volume.class.mq5"

//
// Import Chart Objects ...
#include <Arrays/ArrayObj.mqh>
#include <Controls/Button.mqh>
#include <Controls/Edit.mqh>
#include <Controls/Label.mqh>

//
// Definitions ...

//
// Implementation ...
class XCBaseExpert : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Common ...
    long eaMagicNumber; // Magic Number
    int eaSlippage;     // Slippgae
    string eaLogSuffix; // Log Suffix

    //
    // Symbol Configurations ...
    string eaSymbolConfiguration; // Symbol Configurations ...

    //
    // Management ...
    bool eaAllowGuards;                // Allow Guards
    bool eaAllowTrade;                 // Allow Trade on Signals
    bool eaAllowLongs;                 // Allow Long Trades
    bool eaAllowShorts;                // Allow Short Trades
    double eaMaxAllowedDrawdownFactor; // Max Allowed DrawDown for Trade

    //
    // Volume ...
    ENUM_X_VOLUME_SELECT_TYPE eaVolumeSelect; // Volume Applying Type
    double eaStaticVoluem;                    // Static Volume
    double eaDynamicVolumeStepBalance;        // Step of Balance for Increase Volume
    double eaDynamicVolumeStepVolume;         // Step of Volume Increasing
    double eaConstantRiskBalance;             // Constant Risk Balance per Trade
    double eaConstantPercent;                 // Constant Percent of Balance Per Trade
    double eaConstantBalance;                 // Constant Balance for Calculations

    //
    // Alert ...
    bool eaEnableAlerts;   // Enable Alerts
    bool eaSaveAlerts;     // Save Alerts
    bool eaLogAlerts;      // Log Alerts
    bool eaMailAlerts;     // Mail Alerts
    bool eaPushAlerts;     // Push Alerts
    bool eaTerminalAlerts; // Terminal Alerts

    //
    // Reports ...
    bool eaReportNewMonths;          // Report New Month
    bool eaReportNewWeeks;           // Report New Weeks
    bool eaReportNewDays;            // Report New Days
    bool eaReportNewHours;           // Report New Hours
    bool eaReportTrades;             // Report Trades
    bool eaReportSignals;            // Report Signals
    bool eaReportProtector;          // Report Protector Actions
    bool eaReportRestrictions;       // Report Restrictions
    bool eaReportAfterTradesBalance; // Report Balance after a Trade Finished

    //
    // Collector ...
    bool eaSaveTrades;       // Save Trades
    bool eaSaveSignals;      // Save Signals
    bool eaSaveWins;         // Save Winning Conditions
    bool eaSaveLosts;        // Save Lost Conditions
    bool eaSaveRestrictions; // Save Restriction Reports

    //
    // GUI Management Panel ...
    bool eaShowPanel;                    // Show Management Panel
    double eaPanelAllowedTPSLStep;       // Value for Change TP/SL in Each Step
    double eaPanelMinAllowedRiskInPoint; // Min Allowed Risk in Point
    double eaPanelRiskInPoint;           // Risk in Point per Trades
    double eaPanelRiskToRewardRatio;     // Risk To Reward per Trades
    double eaPanelVolume;                // Default volume Per Trade

    //
    // Requirements ...

    //
    XCAlert *eaAlert;                   // EA Scope Alert Handler ...
    XCGuard *eaGuard;                   // EA Scope Guard Handler ...
    XCTrade *eaTrader;                  // EA Scope Trade Handler ...
    XCVolume *eaVolume;                 // EA Scope Voluem Handler ...
    XCTarget *eaTarget;                 // EA Scope Target Handler ...
    XTimeTracker eaTimeTracker;         // EA Scope Time Tracker ...
    XCTradeManager *eaTradeManager;     // EA Scope Trade Manager Handler ...
    XCPositionDrawer *eaPositionDrawer; // EA Scope Position Drawer ...

    //
    // Specified Event Handlers ...
    TOnGuarded onGuardedEventHandler;
    TCanAnalyse canAnalyseEventHandler;
    TCheckForGuard checkForGuardEventHandler;

    //
    // Tools / Actions / Handlers ...

    //
    // Expert Advisor Event Handlers ...

    /**
     * Handle Initialization of Expert ...
     *
     * @return ( bool )
     */
    bool HandleOnInit()
    {
        //
        bool result = false;

        //
        // Validate Inputs ...
        result = ValidateInputs();
        if (!result)
        {
            return result;
        }

        //
        int count = 0;
        bool has = false;

        //
        // Initialize Alert Handler ...
        eaAlert = new XCAlert();
        eaAlert.SetPrefix(eaLogSuffix);
        eaAlert.SetLogAlerts(eaLogAlerts);
        eaAlert.SetMailAlerts(eaMailAlerts);
        eaAlert.SetPushAlerts(eaPushAlerts);
        eaAlert.SetSaveAlerts(eaSaveAlerts);
        eaAlert.SetEnableAlerts(eaEnableAlerts);
        eaAlert.SetTerminalAlerts(eaTerminalAlerts);

        //
        // Initialize Volume Manager ...
        eaVolume = new XCVolume();
        result = eaVolume.Init(
            eaVolumeSelect,
            eaStaticVoluem,
            eaDynamicVolumeStepBalance,
            eaDynamicVolumeStepVolume,
            eaConstantRiskBalance,
            eaConstantPercent,
            eaConstantBalance //
        );
        if (!result)
        {
            return result;
        }

        //
        // Initialize Trader ...
        eaTrader = new XCTrade(
            eaSlippage,
            eaMagicNumber,
            0,
            0,
            eaMaxAllowedDrawdownFactor //
        );

        //
        // Trade Handler Attachments ...

        //
        // Position Modifiy Event Handlers ...
        count = ArraySize(mOnModifyEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnModifyPositionEventHandler(mOnModifyEventHandlers[i]);
            }
        }

        //
        // Position Stop Loss Event Handlers ...
        count = ArraySize(mStopLossEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnStopLossEventHandler(mStopLossEventHandlers[i]);
            }
        }

        //
        // Position Take Profit Event Handlers ...
        count = ArraySize(mTakeProfitEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnTakeProfitEventHandler(mTakeProfitEventHandlers[i]);
            }
        }

        //
        // Position Force Close Event Handlers ...
        count = ArraySize(mOnForceCloseEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnForceClosePositionEventHandler(mOnForceCloseEventHandlers[i]);
            }
        }

        //
        // Deals Changed Event Handlers ...
        count = ArraySize(mDealsChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnDealsChangedEventHandler(mDealsChangedEventHandlers[i]);
            }
        }

        //
        // Orders Changed Event Handlers ...
        count = ArraySize(mOrdersChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnOrdersChangedEventHandler(mOrdersChangedEventHandlers[i]);
            }
        }

        //
        // Positions Changed Event Handlers ...
        count = ArraySize(mPositionsChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnPositionsChangedEventHandler(mPositionsChangedEventHandlers[i]);
            }
        }

        //
        // Trade Changed Event Handlers ...
        count = ArraySize(mTradeStateChangedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnTradeStateChangedEventHandler(mTradeStateChangedEventHandlers[i]);
            }
        }

        //
        // Postion Partially Close Event Handlers ...
        count = ArraySize(mOnPartialCloseEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnPartialClosePositionEventHandler(mOnPartialCloseEventHandlers[i]);
            }
        }

        //
        // Signal Executed Event Handlers ...
        count = ArraySize(mOnSignalExecutedEventHandlers);
        has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                eaTrader.AddOnSignalExecutedEventHandler(mOnSignalExecutedEventHandlers[i]);
            }
        }

        //
        // Initialize Guard Handler ...
        eaGuard = new XCGuard(
            eaAlert,
            eaTrader //
        );

        //
        // Attach Event Handler ...
        eaGuard.onGuardEventListener = onGuardedEventHandler;

        //
        // Initialize Target Handler ...
        eaTarget = new XCTarget(eaTrader, eaAlert);

        //
        // Init Position Drawer ...
        eaPositionDrawer = new XCPositionDrawer(eaTrader);

        //
        // Initialize Trae Manager ...
        eaTradeManager = new XCTradeManager(
            eaAlert,
            eaTrader,
            eaVolume,
            eaGuard,
            eaTarget //
        );

        //
        // Configuring Trade Manager ...

        //
        // Attache Check For Guard Event Listener ...
        eaTradeManager.checkForGuardEventListener = checkForGuardEventHandler;

        //
        eaTradeManager.SaveWins(eaSaveWins);
        eaTradeManager.SaveLosts(eaSaveLosts);
        eaTradeManager.SaveTrades(eaSaveTrades);
        eaTradeManager.SaveSignals(eaSaveSignals);
        eaTradeManager.SaveRestrictions(eaSaveRestrictions);

        //
        // Setting Trade Reports ...
        eaTradeManager
            .SetTradeReports(
                eaReportTrades,
                eaReportSignals,
                eaReportProtector,
                eaReportRestrictions,
                eaReportAfterTradesBalance //
            );

        //
        // Setting Trade Permissions ...
        eaTradeManager
            .SetTradePermissions(
                eaAllowTrade,
                eaAllowLongs,
                eaAllowShorts //
            );

        //
        // Setting Symbol Configuration ...
        XSymbolTradeConfig symbolConfigurations[];
        eaTradeManager.SetSymbolConfigurations(eaSymbolConfiguration);
        count = eaTradeManager.FillSymbolConfigurations(symbolConfigurations);
        has = IsValidSize(count);
        if (has)
        {
            //
            // Configure Signallers ...
            for (int i = 0; i < count; i++)
            {
                //
                // Register Signallers ...
                PrepareSignallers(symbolConfigurations[i]);
            }
        }
        SpecifiedClean(symbolConfigurations);

        //
        result = InitPanel();
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    /**
     * Handle De Initialization of Expert ...
     */
    void HandleOnDeInit()
    {
        //
        DestroyPanel();

        //
        delete eaTrader;
        ZeroMemory(eaTrader);

        //
        delete eaGuard;
        ZeroMemory(eaGuard);

        //
        delete eaVolume;
        ZeroMemory(eaVolume);

        //
        eaTarget.Destroy();
        delete eaTarget;
        ZeroMemory(eaTarget);

        //
        delete eaTradeManager;
        ZeroMemory(eaTradeManager);

        //
        eaPositionDrawer.Destroy();
        delete eaPositionDrawer;
        ZeroMemory(eaPositionDrawer);

        //
        eaTimeTracker.Clean();

        //
        SpecifiedClean(eaSignallers);
        SpecifiedClean(mOnSignalEventHandlers);
        SpecifiedClean(mOnModifyEventHandlers);
        SpecifiedClean(mStopLossEventHandlers);
        SpecifiedClean(mTakeProfitEventHandlers);
        SpecifiedClean(mOnForceCloseEventHandlers);
        SpecifiedClean(mDealsChangedEventHandlers);
        SpecifiedClean(mOrdersChangedEventHandlers);
        SpecifiedClean(mOnPartialCloseEventHandlers);
        SpecifiedClean(mPositionsChangedEventHandlers);
        SpecifiedClean(mTradeStateChangedEventHandlers);

        //
        string message = "DeInitialized Successfully ...";
        eaAlert.SendAlert(message);

        //
        delete eaAlert;
        ZeroMemory(eaAlert);
    }

    /**
     * Handle Tick ...
     */
    void HandleOnTick()
    {
        //
        // Update GUI Panel ...
        UpdatePanel();

        //
        // Time Report ...
        HandleTimeReport();

        //
        // Manage Trades ...
        eaTradeManager.Manage();

        //
        // Manage Positions Drawings ...
        eaPositionDrawer.Update();

        //
        int count = ArraySize(eaSignallers);
        bool has = IsValidSize(count);
        if (!has)
        {
            return;
        }

        //
        // Loop through Signallers ...
        for (int i = 0; i < count; i++)
        {
            //
            // Calling Process Ticks on Signaller ...
            eaSignallers[i].OnTick(0);
        }

        //
        HandleOnTickAdditional();
    }

    /**
     * Handle Trade ...
     */
    void HandleOnTrade()
    {
        eaTrader.HandleOnTrade();
    }

    /**
     * Handle Timer ...
     */
    void virtual HandleOnTimer()
    {
    }

    /**
     * Handle Chart Event ...
     *
     * @param id: int, Event id ...
     * @param lparam: long, Event Long Parameter ...
     * @param dparam: double, Event Double Parameter ...
     * @param sparam: string, Event String Parameter ...
     */
    void virtual HandleOnChartEvent(
        const int id,
        const long &lparam,
        const double &dparam,
        const string &sparam //
    )
    {
    }

    //
    // Event Triggered Listeners ...

    /**
     * Handle New Signal Recieved ...
     *
     * @param  signal: XSignal, Recieved Signal ...
     */
    void HandleOnSignalTriggered(XSignal &signal)
    {
        eaTradeManager.HandleSignal(signal);
    }

    /**
     * Handle Signal Executed Event ...
     *
     * @param  signal: XSignal, reference to Executed Signal ...
     */
    void HandleOnSignalExecuted(XSignal &signal)
    {
        //
        eaPositionDrawer.AddExecutedSignal(signal);
        eaTradeManager.target.AddExecutedSignal(signal);
    }

    /**
     * Check For Guard ...
     *
     * @param  guards: XGuard, reference Collection to holds result ...
     * @param  positions: XPosition, refrence collection to Provides Positions ...
     * @param  barIndex: int, Specified Bar index ...
     *
     * @return ( int )
     */
    int CheckForGuardTriggered(
        XGuard &guards[],
        XPosition &positions[],
        int barIndex = 0 //
    )
    {
        //
        int result = 0;

        //
        if (!HasChild(eaSignallers))
        {
            return result;
        }

        //
        int count = ArraySize(eaSignallers);
        for (int i = 0; i < count; i++)
        {
            //
            XGuard iGuards[];
            int iGuardsCount = eaSignallers[i].CheckForGuard(
                iGuards,
                positions,
                barIndex //
            );
            if (IsValidSize(iGuardsCount))
            {
                //
                Copy(
                    iGuards,
                    guards,
                    false //
                );
            }

            //
            SpecifiedClean(iGuards);
        }

        //
        result = ArraySize(guards);

        //
        return result;
    }

    /**
     * Handle Guard Events ...
     *
     * @param  action: ENUM_X_GUARD_ACTIONS
     * @param  positions: XPosition[]
     */
    void OnGuardedTriggered(
        ENUM_X_GUARD_ACTIONS action,
        XPosition &positions[] //
    )
    {
        eaTradeManager.HandleGuardEvent(action, positions);
    }

    /**
     * Check Can Analyse Market based on Time ...
     *
     * @param  symbol: string, Specified Symbol ...
     * @param  period: ENUM_TIMEFRAMES, Specified Period ...
     * @param  time: datetime, Specified Time ...
     *
     * @return ( bool )
     */
    bool HandleCanAnalyseEventTriggered(
        string symbol,
        ENUM_TIMEFRAMES period,
        datetime time //
    )
    {
        //
        return eaTradeManager.CanAnalyse(
            symbol,
            period,
            time //
        );
    }

    //
    // Trade Event Handlers ...

    /**
     * Handle Stop Loss Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void virtual HandleOnStopLossTriggered(const XDeal &deal)
    {
        //
        eaTradeManager.HandleSL(deal);
        eaPositionDrawer.HandleStopLossTriggered(deal);
        UpdatePanelPositionButtons();
    }

    /**
     * Handle Take Profit Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void virtual HandleOnTakeProfitTriggered(const XDeal &deal)
    {
        //
        UpdatePanelPositionButtons();
        eaTradeManager.HandleTP(deal);
        eaPositionDrawer.HandleOnTakeProfitTriggered(deal);
    }

    /**
     * Handle Position Force Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param position: XPosition instance refrence, Triggered Position ...
     * @param comment: string, Comment ...
     */
    void virtual HandleOnPositionForceClosed(
        const ulong ticket,
        const XPosition &position,
        const string comment //
    )
    {
        //
        UpdatePanelPositionButtons();
        eaTradeManager.HandleForceClose(position);
        eaPositionDrawer.HandleOnPositionForceClosed(
            ticket,
            position,
            comment //
        );
    }

    /**
     * Handle Position Partially Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param profit: double, Position Profit ...
     * @param comment: string, Comment ...
     */
    void virtual HandleOnPositionPartialClosed(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
        //
        eaTradeManager.HandlePartiallyClosed(
            ticket,
            profit,
            comment //
        );
    }

    /**
     * Handle Deals Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void virtual HandleOnDealsChanged(int count)
    {
    }

    /**
     * Handle Orders Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void virtual HandleOnOrdersChanged(int count)
    {
    }

    /**
     * Handle Positions Changed ...
     *
     * @param count: int, number of changes ...
     * if the count is positive, means new added ...
     * if the count is negative, means removed ...
     */
    void virtual HandleOnPositionsChanged(int count)
    {
        UpdatePanelPositionButtons();
    }

    /**
     * Handle Position Modified Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param profit: double, Position Profit ...
     * @param comment: string, Comment ...
     */
    void virtual HandleOnPositionModified(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
        //
        eaPositionDrawer.HandleOnPositionModified(
            ticket,
            profit,
            comment //
        );
    }

    /**
     * Handle all Trades States Changed Event ...
     *
     * @param state: XOnTradeHandlerState instance refrence, Changed Trade State data ...
     */
    void virtual HandleOnTradeStateChanged(const XOnTradeHandlerState &state)
    {
    }

    //
    // Validators ...

    /**
     * Validate Inputs ...
     *
     * @return ( bool )
     */
    bool virtual ValidateInputs()
    {
        //
        bool result = false;

        //
        // TODO: Handle this ...
        result = true;

        //
        return result;
    }

    //
    // Viruals ...

    /**
     * Prepare Signallers per Symbol Config ...
     *
     * @param  symbolConfig: XSymbolTradeConfig, Symbol Trade Configurations ...
     */
    void virtual PrepareSignallers(XSymbolTradeConfig &symbolConfig)
    {
    }

    /**
     * Additional On Tick Processing if Required ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     */
    void virtual HandleOnTickAdditional(int barIndex = 0)
    {
    }

    //
    // Signal Event Handlers ...

    //
    void AddOnSignalEventHandler(TOnSignal handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnSignalEventHandlers //
        );
    }

    //
    // Add New Signal Executed Event Handler ...
    void AddOnSignalExecutedEventHandler(TOnSignal handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnSignalExecutedEventHandlers //
        );
    }

    //
    // Trade Event Handlers ...

    //
    void AddOnStopLossEventHandler(TOnStopLoss handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mStopLossEventHandlers
            //
        );
    }

    //
    void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTakeProfitEventHandlers
            //
        );
    }

    //
    void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mDealsChangedEventHandlers
            //
        );
    }

    //
    void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOrdersChangedEventHandlers
            //
        );
    }

    //
    void AddOnModifyPositionEventHandler(TOnModify handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnModifyEventHandlers
            //
        );
    }

    //
    void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mPositionsChangedEventHandlers
            //
        );
    }

    //
    void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTradeStateChangedEventHandlers
            //
        );
    }

    //
    void AddOnForceClosePositionEventHandler(TOnForceClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnForceCloseEventHandlers
            //
        );
    }

    //
    void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnPartialCloseEventHandlers
            //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Props ...

    //
    // Actions and Tools ...

    /**
     * Report Time Changes based on Given Configurations ...
     */
    void HandleTimeReport()
    {
        //
        if (eaAlert == NULL)
        {
            return;
        }

        //
        // Monthly Report ....
        if (eaReportNewMonths &&
            eaTimeTracker.IsNewMonth())
        {
            //
            string msg = "New Month ...";

            //
            eaAlert.SendAlert(msg);
        }

        //
        // Weekly Report ....
        if (eaReportNewWeeks &&
            eaTimeTracker.IsNewWeek())
        {
            //
            string msg = "New Week ...";

            //
            eaAlert.SendAlert(msg);
        }

        //
        // Daily Report ....
        if (eaReportNewDays &&
            eaTimeTracker.IsNewDay())
        {
            //
            string msg = "New Day ...";

            //
            eaAlert.SendAlert(msg);
        }

        //
        // Hourly Report ....
        if (eaReportNewHours &&
            eaTimeTracker.IsNewHour())
        {
            //
            string msg = "New Hour ...";

            //
            eaAlert.SendAlert(msg);
        }
    }

    /**
     * Register Signaller ...
     */
    void RegisterSignaller(XCBaseSignaller *signaller)
    {
        //
        // Validate Signaller ...
        if (signaller == NULL)
        {
            return;
        }

        //
        // Attach Signaller OnSignal Event Handlers if Exists ...
        int count = ArraySize(mOnSignalEventHandlers);
        bool has = IsValidSize(count);
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                signaller.AddOnSignalEventHandler(mOnSignalEventHandlers[i]);
            }
        }

        //
        // Attach Can Analyse Event Handler ...
        signaller.canAnalyseEventListener = canAnalyseEventHandler;

        //
        // Register Signaller ...
        Add(
            signaller,
            eaSignallers //
        );
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    // Specified Event Handlers ...
    TOnModify mOnModifyEventHandlers[];
    TOnSignal mOnSignalEventHandlers[];
    TOnStopLoss mStopLossEventHandlers[];
    TOnTakeProfit mTakeProfitEventHandlers[];
    TOnSignal mOnSignalExecutedEventHandlers[];
    TOnForceClose mOnForceCloseEventHandlers[];
    TOnDealsChanged mDealsChangedEventHandlers[];
    TOnOrdersChanged mOrdersChangedEventHandlers[];
    TOnPartialClose mOnPartialCloseEventHandlers[];
    TOnPositionsChanged mPositionsChangedEventHandlers[];
    TOnTradeStateChanged mTradeStateChangedEventHandlers[];

    //
    XCBaseSignaller *eaSignallers[]; // All Registered Signallers ...

    //
    // Panel Support ...

    //
    XCChartHelper *eaChartHelper; // Chart Helper Class ...

    //
    string objSuffix;

    //
    CButton btnBuy;
    CButton btnSell;
    CButton btnClose;
    CButton btnCloseAll;
    CEdit lblPositionId;

    //
    CButton lblTP;
    CButton lblSL;
    CButton btnSLPlus;
    CButton btnTPPlus;
    CButton btnSLMinus;
    CButton btnTPMinus;
    CButton btnSLPlusPlus;
    CButton btnTPPlusPlus;
    CButton btnSLMinusMinus;
    CButton btnTPMinusMinus;

    //
    CArrayObj posArray;
    CButton *btnSelectedPosInfo;
    CArrayObj selectedPosInfoArr;

    //
    ulong selectedPositionID;
    bool showSelectedPositionInfo;

    /**
     * Initial Management GUI Panel ...
     *
     * @return ( bool )
     */
    bool InitPanel()
    {
        //
        bool result = false;

        //
        result = !eaShowPanel;
        if (result)
        {
            return result;
        }

        //
        // Check Default Symbol and Period ...

        //
        int subWindow = 0;
        ulong chartId = ChartID();
        objSuffix = eaLogSuffix + "_";

        //
        // Initial Chart Class Instance ...
        eaChartHelper = new XCChartHelper(chartId);

        //
        int startX = 5;
        int startY = 25;

        //
        int defaultGap = 5;
        int defaultWidth = 100;
        int defaultHeight = 30;

        //
        // Buy ...
        int btnBuyX1 = startX;
        int btnBuyX2 = startX + defaultWidth;
        int btnBuyY1 = startY;
        int btnBuyY2 = startY + defaultHeight;
        string btnBuyName = objSuffix + "BTN_BUY";
        result = btnBuy.Create(
            chartId,
            btnBuyName,
            subWindow,
            btnBuyX1,
            btnBuyY1,
            btnBuyX2,
            btnBuyY2 //
        );
        if (result)
        {
            //
            //  Apply Buy Button Style ...

            //
            btnBuy.Text("Buy");
            btnBuy.Color(clrYellow);
            btnBuy.ColorBackground(clrGreen);
        }

        //
        // Sell ...
        int btnSellX1 = btnBuyX1 + btnBuyX2;
        int btnSellX2 = btnSellX1 + defaultWidth;
        int btnSellY1 = btnBuyY1;
        int btnSellY2 = btnBuyY2;
        string btnSellName = objSuffix + "BTN_SELL";
        result = btnSell.Create(
            chartId,
            btnSellName,
            subWindow,
            btnSellX1,
            btnSellY1,
            btnSellX2,
            btnSellY2 //
        );
        if (result)
        {
            //
            // Apply Sell Button Style ...

            //
            btnSell.Text("Sell");
            btnSell.Color(clrYellow);
            btnSell.ColorBackground(clrDarkRed);
        }

        //
        // LABEL Position ID ...
        int lblPositionIdX1 = btnBuyX1;
        int lblPositionIdX2 = lblPositionIdX1 + defaultWidth;
        int lblPositionIdY1 = btnSellY2 + defaultGap;
        int lblPositionIdY2 = lblPositionIdY1 + defaultHeight;
        string lblPositionIdName = objSuffix + "LBL_POS_ID";
        result = lblPositionId.Create(
            chartId,
            lblPositionIdName,
            subWindow,
            lblPositionIdX1,
            lblPositionIdY1,
            lblPositionIdX2,
            lblPositionIdY2 //
        );
        if (result)
        {
            //
            // Apply Label Position ID Style ...
            lblPositionId.ReadOnly(true);
            lblPositionId.Text("Pos ID: ");
            lblPositionId.Color(clrYellow);
            lblPositionId.ColorBackground(clrDarkGray);
        }

        //
        // Close ...
        int btnCloseX1 = startX;
        int btnCloseX2 = btnCloseX1 + defaultWidth;
        int btnCloseY1 = lblPositionIdY2 + defaultGap;
        int btnCloseY2 = btnCloseY1 + defaultHeight;
        string btnCloseName = objSuffix + "BTN_CLOSE";
        result = btnClose.Create(
            chartId,
            btnCloseName,
            subWindow,
            btnCloseX1,
            btnCloseY1,
            btnCloseX2,
            btnCloseY2 //
        );
        if (result)
        {
            //
            // Apply Close Button Style ...

            //
            btnClose.Text("Close");
            btnClose.Color(clrYellow);
            btnClose.ColorBackground(clrDarkOrange);
        }

        //
        // Close All ...
        int btnCloseAllX1 = btnCloseX2 + defaultGap;
        int btnCloseAllX2 = btnCloseAllX1 + defaultWidth;
        int btnCloseAllY1 = lblPositionIdY2 + defaultGap;
        int btnCloseAllY2 = btnCloseAllY1 + defaultHeight;
        string btnCloseAllName = objSuffix + "BTN_CLOSE_ALL";
        result = btnCloseAll.Create(
            chartId,
            btnCloseAllName,
            subWindow,
            btnCloseAllX1,
            btnCloseAllY1,
            btnCloseAllX2,
            btnCloseAllY2 //
        );
        if (result)
        {
            //
            // Apply Close Button Style ...

            //
            btnCloseAll.Text("Close All");
            btnCloseAll.Color(clrYellow);
            btnCloseAll.ColorBackground(clrDarkRed);
        }

        //
        int minusPlusWidth = 20;

        //
        // TP Label ...
        int lblTPX1 = btnCloseX1;
        int lblTPX2 = lblTPX1 + defaultWidth;
        int lblTPY1 = btnCloseY2 + defaultGap;
        int lblTPY2 = lblTPY1 + defaultHeight;
        string lblTPName = objSuffix + "LBL_TP";
        result = lblTP.Create(
            chartId,
            lblTPName,
            subWindow,
            lblTPX1,
            lblTPY1,
            lblTPX2,
            lblTPY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            lblTP.Disable();
            lblTP.Text("TP");
            lblTP.Color(clrWhite);
            lblTP.ColorBackground(clrGreen);
        }

        //
        // TP Plus ...
        int btnTPPlusX1 = lblTPX1;
        int btnTPPlusX2 = btnTPPlusX1 + minusPlusWidth;
        int btnTPPlusY1 = lblTPY2 + defaultGap;
        int btnTPPlusY2 = btnTPPlusY1 + defaultHeight;
        string btnTPPlusName = objSuffix + "BTN_TP_PLUS";
        result = btnTPPlus.Create(
            chartId,
            btnTPPlusName,
            subWindow,
            btnTPPlusX1,
            btnTPPlusY1,
            btnTPPlusX2,
            btnTPPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPPlus.Text("+");
            btnTPPlus.Color(clrYellow);
            btnTPPlus.ColorBackground(clrGreen);
        }

        //
        // TP Plus  Plus ...
        int btnTPPlusPlusX1 = btnTPPlusX2 + defaultGap;
        int btnTPPlusPlusX2 = btnTPPlusPlusX1 + minusPlusWidth;
        int btnTPPlusPlusY1 = lblTPY2 + defaultGap;
        int btnTPPlusPlusY2 = btnTPPlusPlusY1 + defaultHeight;
        string btnTPPlusPlusName = objSuffix + "BTN_TP_PLUSPLUS";
        result = btnTPPlusPlus.Create(
            chartId,
            btnTPPlusPlusName,
            subWindow,
            btnTPPlusPlusX1,
            btnTPPlusPlusY1,
            btnTPPlusPlusX2,
            btnTPPlusPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPPlusPlus.Text("++");
            btnTPPlusPlus.Color(clrYellow);
            btnTPPlusPlus.ColorBackground(clrGreen);
        }

        //
        // TP Minus ...
        int btnTPMinusX1 = lblTPX2 - minusPlusWidth;
        int btnTPMinusX2 = btnTPMinusX1 + minusPlusWidth;
        int btnTPMinusY1 = lblTPY2 + defaultGap;
        int btnTPMinusY2 = btnTPMinusY1 + defaultHeight;
        string btnTPMinusName = objSuffix + "BTN_TP_MINUS";
        result = btnTPMinus.Create(
            chartId,
            btnTPMinusName,
            subWindow,
            btnTPMinusX1,
            btnTPMinusY1,
            btnTPMinusX2,
            btnTPMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPMinus.Text("-");
            btnTPMinus.Color(clrYellow);
            btnTPMinus.ColorBackground(clrDarkRed);
        }

        //
        // TP Minus Minus ...
        int btnTPMinusMinusX1 = btnTPMinusX1 - minusPlusWidth - defaultGap;
        int btnTPMinusMinusX2 = btnTPMinusMinusX1 + minusPlusWidth;
        int btnTPMinusMinusY1 = lblTPY2 + defaultGap;
        int btnTPMinusMinusY2 = btnTPMinusY1 + defaultHeight;
        string btnTPMinusMinusName = objSuffix + "BTN_TP_MINUSMINUS";
        result = btnTPMinusMinus.Create(
            chartId,
            btnTPMinusMinusName,
            subWindow,
            btnTPMinusMinusX1,
            btnTPMinusMinusY1,
            btnTPMinusMinusX2,
            btnTPMinusMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnTPMinusMinus.Text("--");
            btnTPMinusMinus.Color(clrYellow);
            btnTPMinusMinus.ColorBackground(clrDarkRed);
        }

        //
        // SL Label ...
        int lblSLX1 = btnCloseAllX1;
        int lblSLX2 = lblSLX1 + defaultWidth;
        int lblSLY1 = btnCloseAllY2 + defaultGap;
        int lblSLY2 = lblTPY1 + defaultHeight;
        string lblSLName = objSuffix + "LBL_SL";
        result = lblSL.Create(
            chartId,
            lblSLName,
            subWindow,
            lblSLX1,
            lblSLY1,
            lblSLX2,
            lblSLY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            lblSL.Disable();
            lblSL.Text("SL");
            lblSL.Color(clrWhite);
            lblSL.ColorBackground(clrDarkRed);
        }

        //
        // SL Plus ...
        int btnSLPlusX1 = lblSLX1;
        int btnSLPlusX2 = btnSLPlusX1 + minusPlusWidth;
        int btnSLPlusY1 = lblSLY2 + defaultGap;
        int btnSLPlusY2 = btnSLPlusY1 + defaultHeight;
        string btnSLPlusName = objSuffix + "BTN_SL_PLUS";
        result = btnSLPlus.Create(
            chartId,
            btnSLPlusName,
            subWindow,
            btnSLPlusX1,
            btnSLPlusY1,
            btnSLPlusX2,
            btnSLPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLPlus.Text("+");
            btnSLPlus.Color(clrYellow);
            btnSLPlus.ColorBackground(clrGreen);
        }

        //
        // SL Plus Plus ...
        int btnSLPlusPlusX1 = btnSLPlusX2 + defaultGap;
        int btnSLPlusPlusX2 = btnSLPlusPlusX1 + minusPlusWidth;
        int btnSLPlusPlusY1 = lblSLY2 + defaultGap;
        int btnSLPlusPlusY2 = btnSLPlusPlusY1 + defaultHeight;
        string btnSLPlusPlusName = objSuffix + "BTN_SL_PLUSPLUS";
        result = btnSLPlusPlus.Create(
            chartId,
            btnSLPlusPlusName,
            subWindow,
            btnSLPlusPlusX1,
            btnSLPlusPlusY1,
            btnSLPlusPlusX2,
            btnSLPlusPlusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLPlusPlus.Text("++");
            btnSLPlusPlus.Color(clrYellow);
            btnSLPlusPlus.ColorBackground(clrGreen);
        }

        //
        // SL Minus ...
        int btnSLMinusX1 = lblSLX2 - minusPlusWidth;
        int btnSLMinusX2 = btnSLMinusX1 + minusPlusWidth;
        int btnSLMinusY1 = lblSLY2 + defaultGap;
        int btnSLMinusY2 = btnSLMinusY1 + defaultHeight;
        string btnSLMinusName = objSuffix + "BTN_SL_MINUS";
        result = btnSLMinus.Create(
            chartId,
            btnSLMinusName,
            subWindow,
            btnSLMinusX1,
            btnSLMinusY1,
            btnSLMinusX2,
            btnSLMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLMinus.Text("-");
            btnSLMinus.Color(clrYellow);
            btnSLMinus.ColorBackground(clrDarkRed);
        }

        //
        // SL Minus Minus ...
        int btnSLMinusMinusX1 = btnSLMinusX1 - minusPlusWidth - defaultGap;
        int btnSLMinusMinusX2 = btnSLMinusMinusX1 + minusPlusWidth;
        int btnSLMinusMinusY1 = lblSLY2 + defaultGap;
        int btnSLMinusMinusY2 = btnSLMinusY1 + defaultHeight;
        string btnSLMinusMinusName = objSuffix + "BTN_SL_MINUSMINUS";
        result = btnSLMinusMinus.Create(
            chartId,
            btnSLMinusMinusName,
            subWindow,
            btnSLMinusMinusX1,
            btnSLMinusMinusY1,
            btnSLMinusMinusX2,
            btnSLMinusMinusY2 //
        );
        if (result)
        {
            //
            // Apply Style ...
            btnSLMinusMinus.Text("--");
            btnSLMinusMinus.Color(clrYellow);
            btnSLMinusMinus.ColorBackground(clrDarkRed);
        }

        //
        UpdatePanelState();

        //
        return result;
    }

    /**
     * Listen For Panel Controls Events and
     * Update State of GUI Panel ...
     */
    void UpdatePanel()
    {
        //
        if (!eaShowPanel)
        {
            return;
        }

        //
        UpdatePanelState();

        //
        // Buy Button Pressed ...
        if (btnBuy.Pressed())
        {
            //
            HandleBuyTrade();
            btnBuy.Pressed(false);
        }

        //
        // Sell Button Pressed ...
        if (btnSell.Pressed())
        {
            //
            HandleSellTrade();
            btnSell.Pressed(false);
        }

        //
        // Close Button Pressed ...
        if (btnClose.Pressed())
        {
            //
            if (btnClose.IsEnabled())
            {
                //
                if (selectedPositionID > 0)
                {
                    HandleCloseTrade(selectedPositionID);
                }
            }

            //
            btnClose.Pressed(false);
        }

        //
        // Close All Button Pressed ...
        if (btnCloseAll.Pressed())
        {
            //
            if (btnCloseAll.IsEnabled())
            {
                HandleCloseTrade(0);
            }

            //
            btnCloseAll.Pressed(false);
        }

        //
        // Check Show Hide Selected Position Info ...
        if (btnSelectedPosInfo != NULL)
        {
            //
            if (btnSelectedPosInfo.Pressed())
            {
                //
                // Check Enable ...
                if (btnSelectedPosInfo.IsEnabled())
                {
                    //
                    // Handle Toggle Show/Hide lblSelectedPositionInfo ...
                    CEdit *iLbl = NULL;
                    bool isVisible = showSelectedPositionInfo;
                    int count = selectedPosInfoArr.Total();
                    if (IsValidSize(count))
                    {
                        //
                        // Loop Through Labels ...
                        for (int i = 0; i < count; i++)
                        {
                            //
                            iLbl = (CEdit *)selectedPosInfoArr.At(i);
                            isVisible = iLbl.IsVisible();
                            if (isVisible)
                            {
                                iLbl.Hide();
                            }
                            else
                            {
                                iLbl.Show();
                            }
                        }

                        //
                        // Update Button Text ...
                        isVisible = showSelectedPositionInfo;
                        string btnText = isVisible
                                             ? "Hide Info"
                                             : "Show Info";
                        btnSelectedPosInfo.Text(btnText);
                        showSelectedPositionInfo = !showSelectedPositionInfo;
                    }

                    //
                    ZeroMemory(iLbl);
                }

                //
                btnSelectedPosInfo.Pressed(false);
            }
        }

        //
        // TP Managing ...

        //
        // TP Plus ...
        if (btnTPPlus.Pressed())
        {
            //
            if (btnTPPlus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BULLISH);
            }

            //
            btnTPPlus.Pressed(false);
        }

        //
        // TP Plus Plus ...
        if (btnTPPlusPlus.Pressed())
        {
            //
            if (btnTPPlusPlus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BULLISH, 2);
            }

            //
            btnTPPlusPlus.Pressed(false);
        }

        //
        // TP Minus ...
        if (btnTPMinus.Pressed())
        {
            //
            if (btnTPMinus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BEARISH);
            }

            //
            btnTPMinus.Pressed(false);
        }

        //
        // TP Minus Minus ...
        if (btnTPMinusMinus.Pressed())
        {
            //
            if (btnTPMinusMinus.IsEnabled())
            {
                HandleTPChange(X_DIRECTION_BEARISH, 2);
            }

            //
            btnTPMinusMinus.Pressed(false);
        }

        //
        // SL Managing ...

        //
        // SL Plus ...
        if (btnSLPlus.Pressed())
        {
            //
            if (btnSLPlus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BULLISH);
            }

            //
            btnSLPlus.Pressed(false);
        }

        //
        // SL Plus Plus ...
        if (btnSLPlusPlus.Pressed())
        {
            //
            if (btnSLPlusPlus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BULLISH, 2);
            }

            //
            btnSLPlusPlus.Pressed(false);
        }

        //
        // SL Minus ...
        if (btnSLMinus.Pressed())
        {
            //
            if (btnSLMinus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BEARISH);
            }

            //
            btnSLMinus.Pressed(false);
        }

        //
        // SL Minus Minus ...
        if (btnSLMinusMinus.Pressed())
        {
            //
            if (btnSLMinusMinus.IsEnabled())
            {
                HandleSLChange(X_DIRECTION_BEARISH, 2);
            }

            //
            btnSLMinusMinus.Pressed(false);
        }

        //
        // Check Positions Buttons ...
        int posCount = posArray.Total();
        bool has = IsValidSize(posCount);
        if (has)
        {
            //
            for (int i = 0; i < posCount; i++)
            {
                //
                CButton *iBtn = (CButton *)posArray.At(i);

                //
                if (iBtn.Pressed())
                {
                    //
                    if (iBtn.IsEnabled())
                    {
                        //
                        ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());
                        if (posID > 0 && selectedPositionID != posID)
                        {
                            selectedPositionID = posID;
                            UpdatePanelState();
                        }
                    }

                    //
                    iBtn.Pressed(false);
                }

                //
                ZeroMemory(iBtn);
            }
        }
    }

    /**
     * Update GUI Panels Controls States ...
     */
    void UpdatePanelState()
    {
        //
        if (!eaShowPanel)
        {
            return;
        }
        //
        // Update States Of Position Select Buttons ...
        int count = posArray.Total();
        bool has = IsValidSize(count) && selectedPositionID > 0;
        if (has)
        {
            //
            for (int i = 0; i < count; i++)
            {
                //
                CButton *iBtn = (CButton *)posArray.At(i);

                //
                ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());

                //
                // Select Position ...
                XPosition iPos;
                bool hasPosition = eaTrader.GetPosition(
                    posID,
                    iPos //
                );
                string iTooltip =
                    !hasPosition
                        ? ""
                    : IsLong(iPos.type)
                        ? "Long"
                        : "Short";

                //
                if (posID == selectedPositionID)
                {
                    //
                    iBtn.Disable();
                    iBtn.ColorBackground(clrDarkGray);
                }
                else
                {
                    //
                    iBtn.Enable();

                    //
                    color iBtnBG = IsLong(iPos.type)
                                       ? clrGreen
                                       : clrDarkRed;

                    //
                    iBtn.ColorBackground(iBtnBG);
                }

                //
                ZeroMemory(iBtn);
            }
        }

        //
        // Update State of Close All ...
        if (has && count > 1)
        {
            //
            btnCloseAll.Enable();
            btnCloseAll.ColorBackground(clrDarkRed);
        }
        else
        {
            //
            btnCloseAll.Disable();
            btnCloseAll.ColorBackground(clrDarkGray);
        }

        //
        // Update Selected Position Info Button State ...
        if (has)
        {
            //
            // Reading Global Chart Info ...
            int subWindow = 0;
            int chartWidth = eaChartHelper.Width();
            ulong chartId = eaChartHelper.ChartId();

            //
            // Read Selected Position ...
            XPosition selectedPosition;
            bool hasPosition = eaTrader.GetPosition(
                selectedPositionID,
                selectedPosition //
            );

            //
            // Read All Positions ...
            double profits = 0;
            XPosition positions[];
            string positionsInfo = "";
            int positionsCount = eaTrader.GetPositions(positions);
            has = IsValidSize(positionsCount);
            if (has)
            {
                //
                for (int i = 0; i < positionsCount; i++)
                {
                    profits += positions[i].profit;
                }

                //
                if (positionsCount > 1)
                {
                    positionsInfo = "Positions Count: " + ToXString(positionsCount) + ", Profits: " + ToXString(profits);
                }
            }

            //
            if (hasPosition)
            {
                //
                // Preparing Position Info ...
                string posTicket = "Ticket: " + ToXString(selectedPosition.ticket);
                string posType = "Type: " + ToXString(selectedPosition.type);
                string posSymbolPeriod =
                    "Symbol: " + selectedPosition.symbol + ", " +
                    "Period: " + ToXString(selectedPosition.period);
                string posProfit = "Profit: " + ToXString(selectedPosition.profit);
                string posRiskToRewardRatio = "RiskToReward: " + ToXString(selectedPosition.GetRiskRewardRatio());
                string accountInfo =
                    "Balance: " + ToXString(eaTrader.mAccount.GetBalance()) + ", " +
                    "Equity: " + ToXString(eaTrader.mAccount.GetEquity());

                //
                string posInfos[];

                //
                // Ticket ...
                Add(
                    posTicket,
                    posInfos //
                );

                //
                // Type ...
                Add(
                    posType,
                    posInfos //
                );

                //
                // Symbol Period ...
                Add(
                    posSymbolPeriod,
                    posInfos //
                );

                //
                // Profit ...
                Add(
                    posProfit,
                    posInfos //
                );

                //
                // Risk to Reward Ratio ...
                Add(
                    posRiskToRewardRatio,
                    posInfos //
                );

                //
                // Add Positions Info if Exists ...
                if (IsSpecifiedValid(positionsInfo))
                {
                    //
                    Add(
                        positionsInfo,
                        posInfos //
                    );

                    //
                    // Clear Info Array ...
                    selectedPosInfoArr.Clear();
                }

                //
                // Adding Account Info ...
                Add(
                    accountInfo,
                    posInfos //
                );

                //
                // Create Button If Not Exists ...
                if (btnSelectedPosInfo == NULL)
                {
                    //
                    btnSelectedPosInfo = new CButton();

                    //
                    string name = objSuffix + "BTN_TOOGLE_SPOS_INFO";

                    //
                    CRect iRect = btnTPPlus.Rect();

                    //
                    int x1 = iRect.left;
                    int x2 = x1 + 200 + 5;

                    //
                    int y1 = iRect.bottom + 5;
                    int y2 = y1 + iRect.Height();

                    //
                    bool isCreated = btnSelectedPosInfo.Create(
                        chartId,
                        name,
                        subWindow,
                        x1,
                        y1,
                        x2,
                        y2 //
                    );
                    if (!isCreated)
                    {
                        //
                        delete btnSelectedPosInfo;
                        ZeroMemory(btnSelectedPosInfo);
                    }
                    else
                    {
                        //
                        // Apply Styles ...
                        btnSelectedPosInfo.Color(clrYellow);
                        btnSelectedPosInfo.ColorBackground(clrDarkBlue);
                    }
                }

                //
                // Check Btn Exists ...
                has = btnSelectedPosInfo != NULL;
                if (has)
                {
                    //
                    int posInfoCount = ArraySize(posInfos);
                    int lblInfoCount = selectedPosInfoArr.Total();
                    has = IsValidSize(lblInfoCount) && lblInfoCount == posInfoCount;

                    //
                    // Create info Labels ...
                    if (!has)
                    {
                        //
                        selectedPosInfoArr.Clear();
                        has = IsValidSize(posInfoCount);
                        if (has)
                        {
                            //
                            // Creat Info Control ...

                            //
                            string name = objSuffix + "LBL_SPOS_INFO";

                            //
                            int lblHeight = 25;
                            int lblWidth = 450;

                            //
                            int startX = chartWidth - lblWidth - 5;
                            int sizeX = startX + lblWidth;

                            //
                            int startY = 5;
                            int sizeY = startY + lblHeight;

                            //
                            // Loop through Position Info's Array to Create Label Controls ...
                            for (int i = 0; i < posInfoCount; i++)
                            {
                                //
                                string iText = posInfos[i];
                                string iTextMD5 = ToMD5(iText);

                                //
                                string iName = name + "_" + iTextMD5;

                                //
                                int iX1 = startX;
                                int iX2 = sizeX;

                                //
                                int iY1 = startY;
                                int iY2 = startY + lblHeight;

                                //
                                CEdit *iLbl = new CEdit();
                                bool isCreated = iLbl.Create(
                                    chartId,
                                    iName,
                                    subWindow,
                                    iX1,
                                    iY1,
                                    iX2,
                                    iY2 //
                                );
                                if (isCreated)
                                {
                                    //
                                    iLbl.Text(iText);
                                    iLbl.Color(clrYellow);
                                    iLbl.ColorBorder(clrDarkSlateGray);
                                    iLbl.ColorBackground(clrDarkSlateGray);

                                    //
                                    if (showSelectedPositionInfo)
                                    {
                                        iLbl.Show();
                                    }
                                    else
                                    {
                                        iLbl.Hide();
                                    }

                                    //
                                    selectedPosInfoArr.Add(iLbl);

                                    //
                                    startY = iY2;
                                }
                            }
                        }
                    }

                    //
                    // Update Info Labels ...
                    lblInfoCount = selectedPosInfoArr.Total();
                    has = IsValidSize(lblInfoCount);
                    if (has)
                    {
                        //
                        CEdit *iLbl = NULL;

                        //
                        // Loop Through Infos ...
                        for (int i = 0; i < lblInfoCount; i++)
                        {
                            //
                            iLbl = (CEdit *)selectedPosInfoArr.At(i);

                            //
                            iLbl.Text(posInfos[i]);
                        }

                        //
                        bool isVisible = showSelectedPositionInfo;
                        string btnText = isVisible
                                             ? "Hide Info"
                                             : "Show Info";
                        btnSelectedPosInfo.Text(btnText);

                        //
                        ZeroMemory(iLbl);
                    }
                }

                //
                ZeroMemory(posInfos);
            }

            //
            SpecifiedClean(positions);
        }
        else
        {
            //
            // Toggle Selected Position Btn ...
            if (btnSelectedPosInfo != NULL)
            {
                //
                btnSelectedPosInfo.Destroy();

                //
                delete btnSelectedPosInfo;
                ZeroMemory(btnSelectedPosInfo);
            }

            //
            // Selected Position Lbls ...
            selectedPosInfoArr.Clear();
        }

        //
        // Update State of BtnClose and BtnClose All ...
        if (selectedPositionID == 0)
        {
            //
            btnClose.Disable();
            btnClose.ColorBackground(clrDarkGray);

            //
            lblTP.Disable();
            lblTP.ColorBackground(clrDarkGray);

            //
            btnTPPlus.Disable();
            btnTPPlus.ColorBackground(clrDarkGray);

            //
            btnTPPlusPlus.Disable();
            btnTPPlusPlus.ColorBackground(clrDarkGray);

            //
            btnTPMinus.Disable();
            btnTPMinus.ColorBackground(clrDarkGray);

            //
            btnTPMinusMinus.Disable();
            btnTPMinusMinus.ColorBackground(clrDarkGray);

            //
            lblSL.Disable();
            lblSL.ColorBackground(clrDarkGray);

            //
            btnSLPlus.Disable();
            btnSLPlus.ColorBackground(clrDarkGray);

            //
            btnSLPlusPlus.Disable();
            btnSLPlusPlus.ColorBackground(clrDarkGray);

            //
            btnSLMinus.Disable();
            btnSLMinus.ColorBackground(clrDarkGray);

            //
            btnSLMinusMinus.Disable();
            btnSLMinusMinus.ColorBackground(clrDarkGray);
        }
        else
        {
            //
            btnClose.Enable();
            btnClose.ColorBackground(clrDarkOrange);

            //
            lblTP.Enable();
            lblTP.ColorBackground(clrGreen);

            //
            btnTPPlus.Enable();
            btnTPPlus.ColorBackground(clrGreen);

            //
            btnTPPlusPlus.Enable();
            btnTPPlusPlus.ColorBackground(clrGreen);

            //
            btnTPMinus.Enable();
            btnTPMinus.ColorBackground(clrDarkRed);

            //
            btnTPMinusMinus.Enable();
            btnTPMinusMinus.ColorBackground(clrDarkRed);

            //
            lblSL.Enable();
            lblSL.ColorBackground(clrDarkRed);

            //
            btnSLPlus.Enable();
            btnSLPlus.ColorBackground(clrGreen);

            //
            btnSLPlusPlus.Enable();
            btnSLPlusPlus.ColorBackground(clrGreen);

            //
            btnSLMinus.Enable();
            btnSLMinus.ColorBackground(clrDarkRed);

            //
            btnSLMinusMinus.Enable();
            btnSLMinusMinus.ColorBackground(clrDarkRed);
        }
    }

    /**
     * Update Panel's Position Selector Buttons ...
     */
    void UpdatePanelPositionButtons()
    {
        //
        if (!eaShowPanel)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = eaTrader.GetPositions(
            positions //
        );
        bool has = IsValidSize(count);
        if (!has)
        {
            //
            posArray.Clear();
            selectedPositionID = 0;
            UpdatePanelState();
            return;
        }

        //
        posArray.Clear();

        //
        // Selected Position ID ...
        bool isSelectdPositionIDExists = false;
        if (has && selectedPositionID > 0)
        {
            //
            for (int i = 0; i < count; i++)
            {
                //
                if (!isSelectdPositionIDExists &&
                    positions[i].ticket == selectedPositionID)
                {
                    //
                    isSelectdPositionIDExists = true;
                    break;
                }
            }
        }
        if (!isSelectdPositionIDExists ||
            selectedPositionID == 0)
        {
            //
            XPosition youngest;
            int youngestIDX = GetXYoungest(youngest, positions);
            bool hasYoungest = IsValidIndex(youngestIDX) &&
                               youngest.IsValid();
            if (hasYoungest)
            {
                selectedPositionID = youngest.ticket;
            }

            //
            youngest.Clean();
        }

        //
        int subWindow = 0;
        ulong chartId = eaChartHelper.ChartId();

        //
        int btnWidth = 25;
        int btnHeight = 25;

        //
        CRect iRect = lblPositionId.Rect();

        //
        int lastX = iRect.left + iRect.Width() + 5;
        int lastSizeX = lastX + btnWidth;

        //
        int lastY = iRect.top;
        int lastSizeY = lastY + iRect.Height();

        //
        for (int i = 0; i < count; i++)
        {
            //
            XPosition iPos = positions[i];

            //
            string posTypeStr = IsLong(iPos.type)
                                    ? "LONG"
                                    : "SHORT";

            //
            int iPosBtnX = lastX;
            int iPosBtnSizeX = iPosBtnX + btnWidth;
            string iPosBtnName = objSuffix + "_" + posTypeStr + "_POS_SELECT_BTN_" + ToXString(iPos.ticket);

            //
            CButton *iPosBtn = new CButton();
            has = iPosBtn.Create(
                chartId,
                iPosBtnName,
                subWindow,
                iPosBtnX,
                lastY,
                iPosBtnSizeX,
                lastSizeY //
            );
            if (has)
            {
                //
                // Apply Styles ...
                string iPosBtnText = ToXString(iPos.ticket);

                //
                iPosBtn.Text(iPosBtnText);
                iPosBtn.Color(clrWhite);

                //
                color iPosBtnBG = IsLong(iPos.type)
                                      ? clrGreen
                                      : clrDarkRed;
                iPosBtn.ColorBackground(iPosBtnBG);

                //
                lastX = iPosBtnSizeX + 5;

                //
                if (selectedPositionID == iPos.ticket)
                {
                    iPosBtn.Disable();
                }

                //
                posArray.Add(iPosBtn);
            }

            //
            iPos.Clean();
        }

        //
        ZeroMemory(positions);
        UpdatePanelState();

        //
    }

    /**
     * Destroy Management GUI Panel ...
     */
    void DestroyPanel()
    {
        //
        delete eaChartHelper;
        ZeroMemory(eaChartHelper);

        //
        delete btnSelectedPosInfo;
        ZeroMemory(btnSelectedPosInfo);
    }

    //
    void NormalizePanelProps()
    {
        //
        if (eaPanelVolume < 0.01)
        {
            eaPanelVolume = 0.01;
        }

        //
        if (eaPanelRiskInPoint < 0)
        {
            eaPanelRiskInPoint = 50;
        }

        //
        if (eaPanelAllowedTPSLStep < 5)
        {
            eaPanelAllowedTPSLStep = 5;
        }

        //
        if (eaPanelRiskToRewardRatio < 0.5)
        {
            eaPanelRiskToRewardRatio = 2;
        }

        //
        if (eaPanelMinAllowedRiskInPoint < 10)
        {
            eaPanelMinAllowedRiskInPoint = 10;
        }
    }

    //
    ulong ExtractPositionIDFromButtonName(string name)
    {
        //
        ulong result = 0;

        //
        string nameParts[];
        int namePartsCount = SplitContent(nameParts, name, "_");
        bool has = IsValidSize(namePartsCount);
        if (has)
        {
            //
            string lastPart = nameParts[namePartsCount - 1];
            StringReplace(lastPart, "_", "");
            result = (long)lastPart;
        }

        //
        ZeroMemory(nameParts);

        //
        return result;
    }

    //
    // Management Panel Event Handlers ...

    //
    void HandleBuyTrade()
    {
        HandleTrade(X_DIRECTION_BULLISH);
    }

    //
    void HandleSellTrade()
    {
        HandleTrade(X_DIRECTION_BEARISH);
    }

    //
    void HandleTrade(ENUM_X_DIRECTION dir)
    {
        //
        if (!HasDirection(dir))
        {
            return;
        }

        //
        NormalizePanelProps();

        //
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double _volume = eaPanelVolume;
        double r2r = eaPanelRiskToRewardRatio;
        double _riskInPoints = eaPanelRiskInPoint;
        double point = GetPoints(eaChartHelper.Symbol());
        double entry = GetEntry(eaChartHelper.Symbol(), dir);

        //
        double risk = _riskInPoints * point;
        double reward = risk * r2r;

        //
        double sl = isBullish
                        ? entry - risk
                        : entry + risk;
        double tp = isBullish
                        ? entry + reward
                        : entry - reward;

        //
        string comment = eaLogSuffix + "_POS_" + ToXString(dir);

        //
        if (isBullish)
        {
            //
            eaTrader.Buy(
                eaChartHelper.Symbol(),
                eaChartHelper.Period(),
                _volume,
                entry,
                sl,
                tp,
                comment //
            );
        }
        else
        {
            //
            eaTrader.Sell(
                eaChartHelper.Symbol(),
                eaChartHelper.Period(),
                _volume,
                entry,
                sl,
                tp,
                comment //
            );
        }
    }

    //
    void HandleCloseTrade(ulong positionID)
    {
        //
        if (positionID == 0)
        {
            //
            XPosition positions[];
            int count = eaTrader.GetPositions(
                positions //
            );
            bool has = IsValidSize(count);
            if (has)
            {
                //
                int closedCount = eaTrader.Close(positions);
                has = IsValidSize(closedCount);
            }
        }
        else
        {
            //
            XPosition position;
            bool has = eaTrader.GetPosition(
                positionID,
                position //
            );
            if (has)
            {
                //
                has = eaTrader.Close(positionID);
            }
        }
    }

    //
    void HandleTPChange(
        ENUM_X_DIRECTION dir,
        int multiplier = 1 //
    )
    {
        //
        // Validate ...
        if (selectedPositionID == 0 || !HasDirection(dir))
        {
            return;
        }

        //
        NormalizePanelProps();

        //
        // Normalize Multiplier ...
        multiplier = NormalizeInt(multiplier, 1, 2);

        //
        // Select Position ...
        XPosition position;
        bool has = eaTrader.GetPosition(
            selectedPositionID,
            position //
        );
        if (!has)
        {
            //
            position.Clean();
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double point = GetPoints(position.symbol);
        double stepValue = multiplier * (point * eaPanelAllowedTPSLStep);

        //
        bool mustIncrease =
            isBullish
                ? isLong
                : !isLong;

        //
        double tp = position.tp;
        tp =
            mustIncrease
                ? tp + stepValue
                : tp - stepValue;
        bool isModified = eaTrader.Modify(
            position.ticket,
            position.sl,
            tp,
            objSuffix + "Change TP" //
        );
        if (isModified)
        {
            UpdatePanelState();
        }

        //
        position.Clean();
    }

    //
    void HandleSLChange(
        ENUM_X_DIRECTION dir,
        int multiplier = 1 //
    )
    {
        //
        // Validate ...
        if (selectedPositionID == 0 || !HasDirection(dir))
        {
            return;
        }

        //
        NormalizePanelProps();

        //
        // Normalize Multiplier ...
        multiplier = NormalizeInt(multiplier, 1, 2);

        //
        // Select Position ...
        XPosition position;
        bool has = eaTrader.GetPosition(
            selectedPositionID,
            position //
        );
        if (!has)
        {
            //
            position.Clean();
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isBullish = IsSpecifiedBullish(dir);

        //
        double point = GetPoints(position.symbol);
        double stepValue = multiplier * (point * eaPanelAllowedTPSLStep);

        //
        bool mustIncrease =
            isBullish
                ? !isLong
                : isLong;

        //
        double risk = position.GetRiskInPoint();
        if (risk <= eaPanelMinAllowedRiskInPoint)
        {
            //
            position.Clean();
            return;
        }

        //
        double sl = position.sl;
        sl =
            mustIncrease
                ? sl + stepValue
                : sl - stepValue;
        bool isModified = eaTrader.Modify(
            position.ticket,
            sl,
            position.tp,
            objSuffix + "Change SL" //
        );
        if (isModified)
        {
            UpdatePanelState();
        }

        //
        position.Clean();
    }

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-guard.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class
// -------------------------------------------------
// Name: XCXGuard ...
// Description: Guard Provider Class ...
// Ued in XCTradeManager ...
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm Guard Provider Class"
#property strict

//
// Imports ...
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Libraries/x-saherelm.x-guard.lib.mq5"

//
// Implementation ...
class XCGuard : public XCBase
{
    //
    // public ...
  public:
    //
    // Props ...
    double minVolumeFactorForHedge;          // Minimum Volume Factor for Hedge Profit Calculation ...
    double minProfitPerVolumeFactorForHedge; // Minimum Required Profit per Volume Factor for Hedg ...
    TOnGuarded onGuardEventListener;         // On Guard Event Listener ...

    //
    // Constructor(s) ...
    XCGuard(
        XCAlert *_alert,
        XCTrade *_trader //
    )
    {
        //
        alert = _alert;
        trader = _trader;

        //
        minVolumeFactorForHedge = 0.01;
        minProfitPerVolumeFactorForHedge = 0.5;
    }

    //
    // Deconstructor(s) ...
    ~XCGuard()
    {
        //
        ZeroMemory(alert);
        ZeroMemory(trader);
    }

    //
    // Tools ...
    void DoGuards(XGuard &guards[])
    {
        //
        bool has = HasChild(guards);
        if (!has)
        {
            return;
        }

        //
        XGuard tmp[];
        Copy(
            guards,
            tmp //
        );
        while (HasChild(tmp))
        {
            //
            XGuard iGuard = tmp[0];
            ArrayRemove(
                tmp,
                0,
                1 //
            );

            //
            // Do Guard Action ...
            DoGuard(iGuard);
        }

        //
        Clean(tmp);
    }

    //
    void DoGuard(XGuard &guard)
    {
        //
        bool has = false;

        //
        // Validate and Do Guard ...
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Switch Based on Guard Action ...
        switch (guard.action)
        {
        //
        // Close ...
        case X_GUARD_ACTION_CLOSE:
            DoClose(guard);
            break;

        //
        // Close All ...
        case X_GUARD_ACTION_CLOSE_ALL:
            DoCloseAll(guard);
            break;

        //
        // Close All Long Positions ...
        case X_GUARD_ACTION_CLOSE_LONGS:
            DoCloseLongs(guard);
            break;

        //
        // Close All Short Positions ...
        case X_GUARD_ACTION_CLOSE_SHORTS:
            DoCloseShorts(guard);
            break;

        //
        // Close In Losts ...
        case X_GUARD_ACTION_CLOSE_IN_LOSTS:
            DoCloseInLosts(guard);
            break;

        //
        // Close In Profits ...
        case X_GUARD_ACTION_CLOSE_IN_PROFITS:
            DoCloseInProfits(guard);
            break;

        //
        // Close In Lost Longs ...
        case X_GUARD_ACTION_CLOSE_IN_LOST_LONGS:
            DoCloseInLostLongs(guard);
            break;

        //
        // Close In Profit Longs ...
        case X_GUARD_ACTION_CLOSE_IN_PROFIT_LONGS:
            DoCloseInProfitLongs(guard);
            break;

        //
        // Close In Lost Shorts ...
        case X_GUARD_ACTION_CLOSE_IN_LOST_SHORTS:
            DoCloseInLostShorts(guard);
            break;

        //
        // Close In Profit Shorts ...
        case X_GUARD_ACTION_CLOSE_IN_PROFITS_SHORT:
            DoCloseInProfitShorts(guard);
            break;

        //
        // Partial Close ...
        case X_GUARD_ACTION_PARTIAL_CLOSE:
            DoPartialClose(guard);
            break;

        //
        // Trail Stop ...
        case X_GUARD_ACTION_TRAIL_STOP:
            DoTrailStop(guard);
            break;

        //
        // Trail Target ...
        case X_GUARD_ACTION_TRAIL_TARGET:
            DoTrailTarget(guard);
            break;

        //
        // Hedge Positions ...
        case X_GUARD_ACTION_HEDGE:
            DoHedge(guard);
            break;

            //
        }
    }

    //
    // protected ...
  protected:
    //

    //
    bool GetPosition(
        XPosition &position,
        XGuard &guard //
    )
    {
        //
        bool result = false;

        //
        position.Clean();

        //
        result =
            guard.IsValid() &&
            guard.ticket > 0;
        if (!result)
        {
            return result;
        }

        //
        // Get Position ...
        result = trader.GetPosition(
            guard.ticket,
            position //
        );
        if (!result)
        {
            //
            position.Clean();

            //
            return result;
        }

        //
        // Validate Position ...
        result =
            position.IsValid() &&
            position.symbol == guard.symbol &&
            position.provider == guard.provider &&
            position.period == guard.period;
        if (!result)
        {
            //
            position.Clean();

            //
            return result;
        }

        //
        return result;
    }

    //
    int GetPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        result = trader.GetPositions(
            positions,
            guard.symbol,
            guard.provider,
            guard.period,
            NULL, // All Types ...
            true, // Filter By Magc ...
            true  // Force Clean ...
        );

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetLongPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        result = trader.GetPositions(
            positions,
            guard.symbol,
            guard.provider,
            guard.period,
            X_POSITION_TYPE_LONG, // Specified Types ...
            true,                 // Filter By Magc ...
            true                  // Force Clean ...
        );

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetShortPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        result = trader.GetPositions(
            positions,
            guard.symbol,
            guard.provider,
            guard.period,
            X_POSITION_TYPE_SHORT, // Specified Types ...
            true,                  // Filter By Magc ...
            true                   // Force Clean ...
        );

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInProfitPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit > 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInLostPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit < 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInProfitLongPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetLongPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit > 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInLostLongPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetLongPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit < 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInProfitShortPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetShortPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit > 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    int GetInLostShortPositions(
        XPosition &positions[],
        XGuard &guard //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has = guard.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XPosition allPositions[];
        int allPositionsCount = GetShortPositions(
            allPositions,
            guard //
        );
        has = IsValidSize(allPositionsCount);
        if (!has)
        {
            return result;
        }

        //
        while (HasChild(allPositions))
        {
            //
            XPosition iPosition = allPositions[0];
            ArrayRemove(
                allPositions,
                0,
                1 //
            );

            //
            if (iPosition.profit < 0)
            {
                //
                AddRef(
                    iPosition,
                    positions //
                );
            }

            //
            iPosition.Clean();
        }
        Clean(allPositions);

        //
        result = ArraySize(positions);

        //
        return result;
    }

    //
    // Guard Handlers ...

    //
    void DoClose(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...

        //
        // Validate Action and Requirements ...
        has =
            NotEmpty(guard.ticket) &&
            guard.action == X_GUARD_ACTION_CLOSE;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Position ...
        XPosition position;
        has = GetPosition(
            position,
            guard //
        );
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        string comment = "Guard Close ...";

        //
        has = trader.Close(
            position.ticket,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionGuard(guard.action, position);

            //
            string message =
                "Guard Closed " +
                ToXString(position.type) +
                " Position (" + ToXString(position.ticket) + ") Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        position.Clean();
    }

    //
    void DoCloseAll(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_ALL;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInLosts(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_LOSTS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInLostPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Losts ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Lost Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInProfits(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_PROFITS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInProfitPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Profits ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Profit Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseLongs(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_LONGS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetLongPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close Longs ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " Long Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInLostLongs(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_LOST_LONGS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInLostLongPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Lost Longs ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Lost Longs Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInProfitLongs(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_PROFIT_LONGS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInProfitLongPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Profit Longs ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Profit Longs Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseShorts(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetShortPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close Shorts ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " Long Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInLostShorts(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_LOST_SHORTS;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInLostShortPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Lost Shorts ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Lost Shorts Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoCloseInProfitShorts(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...
        has = guard.action == X_GUARD_ACTION_CLOSE_IN_PROFITS_SHORT;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetInProfitShortPositions(
            positions,
            guard //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Close All In Profit Shorts ...";

        //
        has = trader.Close(
            positions,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            //
            string message =
                "Guard Closed " +
                ToXString(count) +
                " In Profit Shorts Positions Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    void DoPartialClose(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...

        //
        // Validate Action and Requirements ...
        has =
            NotEmpty(guard.ticket) &&
            guard.volumeMultiplier > 0 &&
            guard.action == X_GUARD_ACTION_PARTIAL_CLOSE;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Position ...
        XPosition position;
        has = GetPosition(
            position,
            guard //
        );
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        // Normalize Volume Multiplier ...
        double vMult = guard.volumeMultiplier;
        if (vMult > 0.5)
        {
            vMult = 0.5;
        }
        if (vMult < 0)
        {
            vMult = 0.5;
        }

        //
        // Calculate and Normalize Volume ...
        double volume = position.volume * vMult;
        volume = NormalizeVolume(
            volume,
            position.symbol //
        );

        //
        string comment = "Guard Partially Closed: " + ToXString(volume) + " ...";

        //
        has = trader.ClosePartial(
            position.ticket,
            volume,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionGuard(guard.action, position);

            //
            string message =
                "Guard Closed Partially " +
                ToXString(volume) + " of " +
                ToXString(position.type) +
                " Position (" + ToXString(position.ticket) + ") Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        position.Clean();
    }

    //
    void DoTrailStop(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...

        //
        // Validate Action and Requirements ...
        has =
            guard.sl >= 0 &&
            NotEmpty(guard.ticket) &&
            (guard.sl <= 0
                 ? guard.force
                 : true) &&
            guard.action == X_GUARD_ACTION_TRAIL_STOP;
        if (!has)
        {
            return;
        }

        //
        // Normalize SL ...
        if (guard.sl < 0)
        {
            guard.sl = 0;
        }

        //
        // Retrieve Position ...
        XPosition position;
        has = GetPosition(
            position,
            guard //
        );
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isGuardPassed =
            guard.sl > 0
                ? (isLong
                       ? position.price > guard.sl &&
                             (position.sl == 0 ||
                              position.sl < guard.sl)
                       : position.price < guard.sl &&
                             (position.sl == 0 ||
                              position.sl > guard.sl))
                : (guard.sl == 0 && guard.force);

        //
        // Check Can Trail Stop or not ...
        has =
            //
            // Position Exists ...
            has &&
            //
            // Positions in Profit for Trailling Stop ...
            position.profit > 0 &&
            //
            // Validate SL based on Position Type ...
            isGuardPassed;
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        string comment = "Guard Trail Stop ...";

        //
        has = trader.Modify(
            position.ticket,
            guard.sl,
            position.tp,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionGuard(guard.action, position);

            //
            double before = NormalizePrice(position.sl, position.symbol);
            double after = NormalizePrice(guard.sl, position.symbol);

            //
            string message =
                "Guard Trail Stop " +
                ToXString(position.type) +
                " Position (" + ToXString(position.ticket) + ")" +
                " From: " + ToXString(before) +
                "To: " + ToXString(after) +
                " Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        position.Clean();
    }

    //
    void DoTrailTarget(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...

        //
        // Validate Action and Requirements ...
        has =
            guard.tp >= 0 &&
            NotEmpty(guard.ticket) &&
            (guard.tp <= 0
                 ? guard.force
                 : true) &&
            guard.action == X_GUARD_ACTION_TRAIL_TARGET;
        if (!has)
        {
            return;
        }

        //
        // Normalize ...
        if (guard.tp < 0)
        {
            guard.tp = 0;
        }

        //
        // Retrieve Position ...
        XPosition position;
        has = GetPosition(
            position,
            guard //
        );
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        bool isLong = IsLong(position.type);
        bool isGuardPassed =
            guard.tp > 0
                ? (isLong
                       ? guard.tp > position.price
                       : guard.tp < position.price)
                : (guard.tp == 0 && guard.force);

        //
        // Check Can Trail Stop or not ...
        has =
            //
            // Position Exists ...
            has &&
            //
            // Check TP is not Same ...
            guard.tp != position.tp &&
            //
            // Validate TP based on Current Price ...
            isGuardPassed;
        if (!has)
        {
            //
            position.Clean();

            //
            return;
        }

        //
        string comment = "Guard Trail Target ...";

        //
        has = trader.Modify(
            position.ticket,
            position.sl,
            guard.tp,
            comment //
        );
        if (has)
        {
            //
            HandleNotifyPositionGuard(guard.action, position);

            //
            double before = NormalizePrice(position.tp, position.symbol);
            double after = NormalizePrice(guard.tp, position.symbol);

            //
            string message =
                "Guard Trail Target " +
                ToXString(position.type) +
                " Position (" + ToXString(position.ticket) + ")" +
                " From: " + ToXString(before) +
                "To: " + ToXString(after) +
                " Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        position.Clean();
    }

    //
    void DoHedge(XGuard &guard)
    {
        //
        bool has = false;

        //
        has = guard.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Do What we want ...

        //
        // Validate Action and Requirements ...
        has =
            (guard.force
                 ? true
                 : (minVolumeFactorForHedge > 0 &&
                    minProfitPerVolumeFactorForHedge > 0)) &&
            guard.action == X_GUARD_ACTION_HEDGE;
        if (!has)
        {
            return;
        }

        //
        // Retrieve Positions ...
        XPosition positions[];
        int count = GetPositions(
            positions,
            guard //
        );
        has = HasChild(positions);
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        // Calculate Positions Profits and Volumes ...
        double swaps = 0;
        double profits = 0;
        double volumes = 0;
        double commissions = 0;
        for (int i = 0; i < count; i++)
        {
            //
            swaps += positions[i].swap;
            profits += positions[i].profit;
            volumes += positions[i].volume;
            commissions += positions[i].commission;
        }

        //
        // Check Profits and Volumes for Hedging ...
        double hedgeValue = profits + (-1 * swaps) + commissions;

        //
        // Check Force Hedging ...
        if (!guard.force)
        {
            //
            // Calculate Minimum Required Profit for Hedging ...
            double minRequiredProfit =
                (volumes / minVolumeFactorForHedge) * minProfitPerVolumeFactorForHedge;

            //
            has = hedgeValue >= minRequiredProfit;
        }
        else
        {
            has = hedgeValue > 0;
        }

        //
        if (!has)
        {
            //
            Clean(positions);

            //
            return;
        }

        //
        string comment = "Guard Hedge ...";

        //
        // Loop through Positions and Close them One by One ...
        // this is because of Trade Manager to Handle Close Trades ...
        int closedCount = 0;
        for (int i = 0; i < count; i++)
        {
            //
            has = trader.Close(
                positions[i].ticket,
                comment //
            );
            if (has)
            {
                closedCount++;
            }
        }

        //
        has = IsValidSize(closedCount);
        if (has)
        {
            //
            HandleNotifyPositionsGuard(guard.action, positions);

            //
            string message =
                "Guard Hedge " +
                ToXString(closedCount) +
                " Positions by: " +
                ToXString(hedgeValue) +
                " Successfully ...";

            //
            alert.SendAlert(message);
        }

        //
        // Cleanup Resources ...

        //
        Clean(positions);
    }

    //
    // private ...
  private:
    //
    XCTrade *trader;
    XCAlert *alert;

    //
    void HandleNotifyPositionGuard(
        ENUM_X_GUARD_ACTIONS action,
        XPosition &position //
    )
    {
        //
        if (!IsSpecifiedValid(action) || !position.IsValid() || onGuardEventListener == NULL)
        {
            return;
        }

        //
        XPosition positions[];
        AddRef(
            position,
            positions //
        );
        HandleNotifyPositionsGuard(action, positions);
    }

    //
    void HandleNotifyPositionsGuard(
        ENUM_X_GUARD_ACTIONS action,
        XPosition &positions[] //
    )
    {
        //
        if (!IsSpecifiedValid(action) || !HasChild(positions) || onGuardEventListener == NULL)
        {
            return;
        }

        //
        onGuardEventListener(action, positions);
    }

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-helper.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCBaseHelper
// Description: provides all Base Indicator
// Helper requirements ...
//
//
// 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

//
// Includes ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"

//
// Definitions ...
enum ENUM_X_SCORE
{
    X_SCORE_NONE = 0,
    X_SCORE_MIN = 1,
    X_SCORE_NORMAL = 2,
    X_SCORE_MAX = 3,
};

// #region Generics ...
//
//  State Handler Generic Functions ...
#define DECLARE_STATE_FUNCTIONS(name, state_reader, bar_normalizer_func)        \
    bool Is##name##Bullish(int barIndex = 0)                                    \
    {                                                                           \
        barIndex = bar_normalizer_func(barIndex);                               \
        return state_reader(barIndex) >= 1;                                     \
    }                                                                           \
    bool Is##name##Bearish(int barIndex = 0)                                    \
    {                                                                           \
        barIndex = bar_normalizer_func(barIndex);                               \
        return state_reader(barIndex) <= -1;                                    \
    }                                                                           \
    bool Is##name##SwitchedToBullish(int barIndex = 0)                          \
    {                                                                           \
        barIndex = bar_normalizer_func(barIndex);                               \
        return Is##name##Bullish(barIndex) && !Is##name##Bullish(barIndex + 1); \
    }                                                                           \
    bool Is##name##SwitchedToBearish(int barIndex = 0)                          \
    {                                                                           \
        barIndex = bar_normalizer_func(barIndex);                               \
        return Is##name##Bearish(barIndex) && !Is##name##Bearish(barIndex + 1); \
    }

//
// State Handler Based On Price Type Generic Functions ...
#define DECLARE_PRICE_BASED_STATE_FUNCTIONS(name, value_reader, bar_normalizer_func, point_reader_func, price_reader_func)  \
    bool Is##name##Bullish(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE, double tolerance = 2)                  \
    {                                                                                                                       \
        barIndex = bar_normalizer_func(barIndex);                                                                           \
        double threshold = point_reader_func() * tolerance;                                                                 \
        return price_reader_func(barIndex, priceType) > value_reader(barIndex) + threshold ||                               \
               price_reader_func(barIndex, priceType) > value_reader(barIndex) - threshold;                                 \
    }                                                                                                                       \
    bool Is##name##Bearish(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE, double tolerance = 2)                  \
    {                                                                                                                       \
        barIndex = bar_normalizer_func(barIndex);                                                                           \
        double threshold = point_reader_func() * tolerance;                                                                 \
        return price_reader_func(barIndex, priceType) < value_reader(barIndex) + threshold ||                               \
               price_reader_func(barIndex, priceType) < value_reader(barIndex) - threshold;                                 \
    }                                                                                                                       \
    bool Is##name##SwitchedToBullish(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE, double tolerance = 2)        \
    {                                                                                                                       \
        barIndex = bar_normalizer_func(barIndex);                                                                           \
        return Is##name##Bullish(barIndex, priceType, tolerance) && !Is##name##Bullish(barIndex + 1, priceType, tolerance); \
    }                                                                                                                       \
    bool Is##name##SwitchedToBearish(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE, double tolerance = 2)        \
    {                                                                                                                       \
        barIndex = bar_normalizer_func(barIndex);                                                                           \
        return Is##name##Bearish(barIndex, priceType, tolerance) && !Is##name##Bearish(barIndex + 1, priceType, tolerance); \
    }

//
// Value Comparator Generic Functions ...
#define DECLARE_VALUE_COMPARATOR_FUNCTIONS(name, value_reader, bar_normalizer_func, point_reader_func)    \
    bool Is##name##OverLast(int barIndex = 0, double tolerance = 2)                                       \
    {                                                                                                     \
        barIndex = bar_normalizer_func(barIndex);                                                         \
        return IsOver(                                                                                    \
            value_reader(barIndex),                                                                       \
            value_reader(barIndex + 1),                                                                   \
            point_reader_func() * tolerance);                                                             \
    }                                                                                                     \
    bool Is##name##UnderLast(int barIndex = 0, double tolerance = 2)                                      \
    {                                                                                                     \
        barIndex = bar_normalizer_func(barIndex);                                                         \
        return IsUnder(                                                                                   \
            value_reader(barIndex),                                                                       \
            value_reader(barIndex + 1),                                                                   \
            point_reader_func() * tolerance);                                                             \
    }                                                                                                     \
    bool Is##name##SameAsLast(int barIndex = 0, double tolerance = 2)                                     \
    {                                                                                                     \
        barIndex = bar_normalizer_func(barIndex);                                                         \
        return IsSame(                                                                                    \
            value_reader(barIndex),                                                                       \
            value_reader(barIndex + 1),                                                                   \
            point_reader_func() * tolerance);                                                             \
    }                                                                                                     \
    bool Is##name##CrossedOverLast(int barIndex = 0, double tolerance = 2)                                \
    {                                                                                                     \
        barIndex = bar_normalizer_func(barIndex);                                                         \
        return Is##name##OverLast(barIndex, tolerance) && !Is##name##OverLast(barIndex + 1, tolerance);   \
    }                                                                                                     \
    bool Is##name##CrossedUnderLast(int barIndex = 0, double tolerance = 2)                               \
    {                                                                                                     \
        barIndex = bar_normalizer_func(barIndex);                                                         \
        return Is##name##UnderLast(barIndex, tolerance) && !Is##name##UnderLast(barIndex + 1, tolerance); \
    }

//
// Price Comparator Generic Functions ...
#define DECLARE_PRICE_COMPARATOR_FUNCTIONS(name, value_reader, bar_normalizer_func, price_reader_func)  \
    bool IsPriceOver##name(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE)                    \
    {                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                       \
        return price_reader_func(barIndex, priceType) > value_reader(barIndex);                         \
    }                                                                                                   \
    bool IsPriceUnder##name(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE)                   \
    {                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                       \
        return price_reader_func(barIndex, priceType) < value_reader(barIndex);                         \
    }                                                                                                   \
    bool IsPriceCrossedOver##name(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE)             \
    {                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                       \
        return IsPriceOver##name(barIndex, priceType) && !IsPriceOver##name(barIndex + 1, priceType);   \
    }                                                                                                   \
    bool IsPriceCrossedUnder##name(int barIndex = 0, ENUM_X_PRICE priceType = X_PRICE_CLOSE)            \
    {                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                       \
        return IsPriceUnder##name(barIndex, priceType) && !IsPriceUnder##name(barIndex + 1, priceType); \
    }

//
// Crossing Comparator Generic Functions ...
#define DECLARE_CROSSING_COMPARATOR_FUNCTIONS(baseName, fastName, slowName, fast_reader_func, slow_reader_func, bar_normalizer_func, point_reader_func) \
    bool Is##baseName##fastName##Over##Slow(int barIndex = 0, double tolerance = 2)                                                                     \
    {                                                                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                                                                       \
        double fast = fast_reader_func(barIndex);                                                                                                       \
        double slow = slow_reader_func(barIndex);                                                                                                       \
        double distance = tolerance * point_reader_func();                                                                                              \
        return fast > slow && MathAbs(fast - slow) >= distance;                                                                                         \
    }                                                                                                                                                   \
    bool Is##baseName##fastName##Under##Slow(int barIndex = 0, double tolerance = 2)                                                                    \
    {                                                                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                                                                       \
        double fast = fast_reader_func(barIndex);                                                                                                       \
        double slow = slow_reader_func(barIndex);                                                                                                       \
        double distance = tolerance * point_reader_func();                                                                                              \
        return fast < slow && MathAbs(fast - slow) >= distance;                                                                                         \
    }                                                                                                                                                   \
    bool Is##baseName##fastName##CrossedOver##Slow(int barIndex = 0, double tolerance = 2)                                                              \
    {                                                                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                                                                       \
        return Is##baseName##fastName##Over##Slow(barIndex, tolerance) && !Is##baseName##fastName##Over##Slow(barIndex + 1, tolerance);                 \
    }                                                                                                                                                   \
    bool Is##baseName##fastName##CrossedUnder##Slow(int barIndex = 0, double tolerance = 2)                                                             \
    {                                                                                                                                                   \
        barIndex = bar_normalizer_func(barIndex);                                                                                                       \
        return Is##baseName##fastName##Under##Slow(barIndex, tolerance) && !Is##baseName##fastName##Under##Slow(barIndex + 1, tolerance);               \
    }
// #endregion

//
// a Class for Handle base requirements ...
// for indicators ...
class XCBaseHelper : public XCBase
{
    //
    // Public ...
  public:
    //
    // Constructor ...
    XCBaseHelper(
        string symbol,         // Trading Symbol
        ENUM_TIMEFRAMES period // Trading Time Frame
    )
    {
        //
        mSymbol = symbol;
        mPeriod = period;
    }

    //
    // Deconstructor ...
    ~XCBaseHelper()
    {
        //
        IndicatorRelease(mHandler);
    }

    //
    // Setter(s) / Getter(s) ...

    //
    // Symbol ...
    string GetSymbol()
    {
        return mSymbol;
    }

    //
    // Period ...
    ENUM_TIMEFRAMES GetPeriod()
    {
        return mPeriod;
    }

    //
    // Retrieve Bars ...
    int CountBars()
    {
        //
        int result =
            Bars(
                mSymbol,
                mPeriod
                //
            );

        //
        return result;
    }

    //
    // Retrieve Indicator Calculated Bars ...
    int CountCalculatedBars()
    {
        return BarsCalculated(mHandler);
    }

    //
    virtual void Free()
    {
    }

    //
    // Generate Tag ...
    virtual string GetTag()
    {
        //
        string result = NULL;

        //
        result =
            //
            GetToken() +
            "[" +
            GetSymbol() + "|" +
            ToXString(GetPeriod()) +
            "]"
            //
            ;

        //
        return result;
    }

    //
    // Functions ...

    /**
     * Validate Handler Exists ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result = IsXValid(mSymbol) &&
                 IsXValid(mPeriod) &&
                 mHandler != INVALID_HANDLE;

        //
        return result;
    }

    /**
     * Extract Validated Zones ...
     *
     * @param  zones: XBoxZone, collection reference to holds results ...
     * @param  barIndex: int, Specified Bar Index ...
     * @param  requiredNumberOFZones: int, required Number of Zones ...
     * @param  breakValidLength: int, Breake Validation Length of Zones ...
     * @param  validateFakeBreakes: bool, Validation Fake Breaked Zones ...
     * @param  shadowMultiplier: double a Shadow Multiplier for Validation ...
     * @param  validationStrength: double, Minimum Strngth Multiplier in Point to Validate ...
     * @param  validationLength: int, Validation Length of Zones ...
     * @param  maxAllowedLoopbackLength: int, max Allowed Loopback Length ...
     *
     * @return ( int )
     */
    int ExtractBarZones(
        XBoxZone &zones[],
        int barIndex = 0,
        int requiredNumberOFZones = 50,
        int breakValidLength = 3,
        bool validateFakeBreakes = true,
        double shadowMultiplier = 3,
        double validationStrength = 1,
        int validationLength = 21,
        int maxAllowedLoopbackLength = 1500 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        SpecifiedClean(zones);

        //
        // Normalize ...
        barIndex = NormalizeInt(barIndex, 0);
        validationLength = NormalizeInt(validationLength, 7);
        shadowMultiplier = NormalizeDouble(shadowMultiplier, 1);
        validationStrength = NormalizeDouble(validationStrength, 1);
        requiredNumberOFZones = NormalizeInt(requiredNumberOFZones, 5);
        maxAllowedLoopbackLength = NormalizeInt(maxAllowedLoopbackLength, 100);

        //
        // Reading Values ...

        //
        string symbol = GetSymbol();
        ENUM_TIMEFRAMES period = GetPeriod();
        double minAllowedStrength = (validationStrength * GetPoints(symbol));

        //
        XOHCL bar;
        bool has = false;

        //
        // Initialized Start Bar ...
        has = bar.Init(
            symbol,
            period,
            barIndex //
        );
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        XBoxZone zone;
        int start = barIndex;
        bool isBullish = false;
        bool isBearish = false;
        bool lowShadowPassed = false;
        bool highShadowPassed = false;
        ENUM_X_DIRECTION dir = X_DIRECTION_NONE;
        int end = start + maxAllowedLoopbackLength;
        for (int i = start; i < end; i++)
        {
            //
            // Cleanup ...
            iBar.Clean();
            zone.Clean();
            dir = X_DIRECTION_NONE;

            //
            // Reading HK Buffesr ...
            has = bar.BarAt(i, iBar);
            if (!has)
            {
                continue;
            }

            //
            // Check Direction ...
            isBullish = iBar.open < iBar.close;
            isBearish = iBar.open > iBar.close;
            dir =
                (isBullish && !isBearish)
                    ? X_DIRECTION_BULLISH
                : (isBearish && !isBullish)
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
            has = HasDirection(dir);
            if (!has)
            {
                continue;
            }

            //
            // Low Shadow ...
            lowShadowPassed = iBar.GetLowShadow() >= (shadowMultiplier * iBar.GetHighShadow());

            //
            // High Shadow ...
            highShadowPassed = iBar.GetHighShadow() >= (shadowMultiplier * iBar.GetLowShadow());

            //
            has = (lowShadowPassed && !highShadowPassed) ||
                  (highShadowPassed && !lowShadowPassed);
            if (!has)
            {
                continue;
            }

            //
            // Filling Zone ...

            //
            zone.symbol = symbol;
            zone.period = period;
            zone.from = GetBarTime(
                zone.symbol,
                zone.period,
                i //
            );
            zone.dir = lowShadowPassed
                           ? X_DIRECTION_BULLISH
                       : highShadowPassed
                           ? X_DIRECTION_BEARISH
                           : X_DIRECTION_NONE;

            //
            zone.upper =
                IsXBullish(zone.dir)
                    ? iBar.GetDown()
                : IsXBearish(zone.dir)
                    ? iBar.high
                    : EMPTY_VALUE;

            //
            zone.lower =
                IsXBullish(zone.dir)
                    ? iBar.low
                : IsXBearish(zone.dir)
                    ? iBar.GetUp()
                    : EMPTY_VALUE;

            //
            zone.to = bar.time;
            zone.type = "XBar_" + (IsXBullish(zone.dir) ? "Support" : IsXBearish(zone.dir) ? "Resistance"
                                                                                           : "") +
                        "_" + ToXString(TimeToSeconds(zone.from));

            //
            // Validate ...
            has =
                zone.IsValid() &&
                HasDirection(zone.dir) &&
                NotEmptyZero(zone.upper) &&
                NotEmptyZero(zone.lower) &&
                (zone.IsBullish()
                     ? bar.low > zone.upper
                     : bar.high < zone.lower) &&
                (zone.upper - zone.lower) >= minAllowedStrength &&
                ValidateBarZone(zone, validationLength, breakValidLength, validateFakeBreakes);
            if (has)
            {
                //
                AddIfNotExists(
                    zone,
                    zones //
                );
            }

            //
            // Cleanup Resources ...
            zone.Clean();

            //
            // Validating ...
            result = ArraySize(zones);
            has = IsValidSize(result) &&
                  result >= requiredNumberOFZones;
            if (has)
            {
                break;
            }
        }

        //
        // Cleanup Resources ...
        bar.Clean();
        iBar.Clean();
        zone.Clean();

        //
        return result;
    }

    /**
     * Validate Bar Zone ...
     *
     * @param  zone: XBoxZone, reference to Specified Zone ...
     * @param  validationLength: int, Validation Length of Zones ...
     * @param  breakValidLength: int, Break Validation Length of Zones ...
     * @param  validateFakeBreakes: bool, Validation Fake Breaked Zones ...
     *
     * @return ( bool )
     */
    bool ValidateBarZone(
        XBoxZone &zone,
        int validationLength = 1,
        int breakValidLength = 3,
        bool validateFakeBreakes = true //
    )
    {
        //
        bool result = false;

        //
        // Normalize ...
        validationLength = NormalizeInt(validationLength, 1);
        breakValidLength = NormalizeInt(breakValidLength, 2);

        //
        // Validate ...
        result = zone.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Requirements ...
        XOHCL iBar;
        int count = 0;
        string symbol = GetSymbol();
        ENUM_TIMEFRAMES period = GetPeriod();

        //
        int start = zone.FromIndex() + 1;
        int end = start + validationLength;

        //
        // Validate Before ...
        for (int i = start; i < end; i++)
        {
            //
            // Initialize Indexed Bar ...
            iBar.Clean();
            result = iBar.Init(
                symbol,
                period,
                i //
            );
            if (!result)
            {
                break;
            }

            //
            result = zone.IsBullish()
                         ? iBar.low > zone.lower
                         : iBar.high < zone.upper;
            if (!result)
            {
                break;
            }
        }

        //
        // Validating After ...
        if (result)
        {
            //
            start = zone.ToIndex() + 1;
            count = zone.FromIndex() - start;
            end = start + count;
            for (int i = start; i < end; i++)
            {
                //
                // Initialized Indexed Bar ...
                iBar.Clean();
                result = iBar.Init(
                    symbol,
                    period,
                    i //
                );
                if (!result)
                {
                    break;
                }

                //
                result = zone.IsBullish()
                             ? iBar.low > zone.lower
                             : iBar.high < zone.upper;
                if (!result)
                {
                    //
                    // Check For Fake Breake ...
                    if (validateFakeBreakes && breakValidLength > 0)
                    {
                        //
                        result = i - 1 >= 0;
                        if (result)
                        {
                            //
                            for (int j = i - 1; j >= zone.ToIndex(); j--)
                            {
                                //
                                // Initialized Indexed Bar ...
                                iBar.Clean();
                                result = iBar.Init(
                                    symbol,
                                    period,
                                    j //
                                );
                                if (!result)
                                {
                                    break;
                                }

                                //
                                result = zone.IsBullish()
                                             ? iBar.close > zone.upper
                                             : iBar.close < zone.lower;
                                if (result)
                                {
                                    //
                                    result = MathAbs(j - i) <= breakValidLength;
                                    if (result)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    //
                    if (!result)
                    {
                        break;
                    }
                }
            }
        }

        //
        // Cleanup Resource ...
        iBar.Clean();

        //
        return result;
    }

    /**
     * Normalizing Bar Index ...
     *
     * @param  barIndex: int, Provided Bar Index ...
     *
     * @return ( int )
     */
    int NormalizeBarIndex(int barIndex)
    {
        //
        int result = barIndex;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        int count = CountBars() - 1;
        result = NormalizeInt(result, 0, count - 1);

        //
        return result;
    }

    //
    // Protected ...
  protected:
    //
    // Props ...

    //
    // Symbol ...
    string mSymbol;

    //
    // Period ...
    ENUM_TIMEFRAMES mPeriod;

    //
    // Indicator Handler ...
    int mHandler;

    /**
     * Read Value of Specified Buffer ...
     *
     * @param  bufferIndex: int, which handler buffer to read value ...
     * @param  barIndex: int, Specified Bar Index ...
     *
     * @return ( double )
     */
    double ReadValue(
        int bufferIndex,
        int barIndex = 0 //
    )
    {
        //
        double result = EMPTY_VALUE;

        //
        // Normalize ...
        int barsTotal = CountBars();
        barIndex = NormalizeInt(barIndex, 0, barsTotal - 1);
        bufferIndex = NormalizeInt(bufferIndex, 0);

        //
        // Validate ...
        if (!IsValid())
        {
            return result;
        }

        //
        double tmp[];
        int count = CopyBuffer(
            mHandler,
            bufferIndex,
            barIndex,
            1,
            tmp //
        );
        result = tmp[0];

        //
        return result;
    }

    /**
     * Read Values of Specified Buffer ...
     *
     * @param  buffer: double, reference collection to holds result ...
     * @param  bufferIndex: int, which handler buffer to read value ...
     * @param  barIndex: int, Specified Bar Index ...
     * @param  count: int, number of items to read, 0 for WHOLE_ARRAY ...
     * @param  asSeries: bool, set As Series Buffer or not ...
     *
     * @return ( int )
     */
    int ReadValues(
        double &buffer[],
        int bufferIndex,
        int barIndex = 0,
        int count = 0,
        bool asSeries = true //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(buffer);

        //
        // Normalize ...
        int barsTotal = CountBars();
        count = NormalizeInt(count, 0);
        bufferIndex = NormalizeInt(bufferIndex, 0);
        barIndex = NormalizeInt(barIndex, 0, barsTotal - 1);

        //
        // Validate ...
        if (!IsValid())
        {
            return result;
        }

        //
        result = CopyBuffer(
            mHandler,
            bufferIndex,
            barIndex,
            count,
            buffer //
        );

        //
        // Set As Series Flag ...
        ArraySetAsSeries(buffer, asSeries);

        //
        result = ArraySize(buffer);

        //
        return result;
    }

    //
    // Private ...
  private:
    //
};

//
// Tools ....


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-http.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCHttp
// Description: provides all HTTP requirements ...
//
//
// 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

//
// Definitions ...

//
enum X_HTTP_METHOD
{
    X_HTTP_GET,
    X_HTTP_POST
};

//
// Imports ...

//
// Includes ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// a Class for Manage Account ...
class XCHttp : public XCBase
{
    //
    // Public ...
  public:
    //
    // Constructor ...
    XCHttp()
    {
        XCHttp("", 10000);
    }
    XCHttp(
        string path, // Base Folder to Store Data
        int timeout  // base timeout for Requests
    )
    {
        //
        Path(path);
        Timeout(timeout);
    }

    //
    // Deconstructor ...
    ~XCHttp()
    {
    }

    //
    // Properties Getter(s) / Setter(s) ...

    //
    // Path ...
    void Path(string value)
    {
        //
        mPath = value;

        //
        if (!IsValid(mPath))
        {
            mPath = GetTag();
        }
    }

    string Path()
    {
        return mPath;
    }

    //
    // Timeout ...
    void Timeout(int value)
    {
        mTimeout = value;
    }

    //
    int Timeout()
    {
        return mTimeout;
    }

    //
    // Error ...
    int Error()
    {
        return mError;
    }

    //
    // Response ...
    string Response()
    {
        return mResponse;
    }

    //
    // Overrides ...
    string GetTag() override
    {
        return GetSpecificToken(this);
    }

    //
    // Tools ...

    //
    // Send Global Request ...
    int SendRequest(
        X_HTTP_METHOD method,   // Httm Request Method
        const string url,       // Server Address
        const string headers,   // Headers providing
        const char &payload[],  // the Data which needs to Send
        char &response[],       // Response of request
        string responseHeaders, // Response Headers
        int timeout = 500       // Timeout for response default is 500
    )
    {
        //
        int result = -1;

        //
        string strMethod = ToString(method);
        if (StringLen(strMethod) == 0)
        {
            return result;
        }

        //
        // Reset State ...
        ResetState();

        //
        result = WebRequest(
            strMethod,
            url,
            headers,
            timeout,
            payload,
            response,
            responseHeaders);

        //
        if (result < 0)
        {
            mError = GetLastError();
        }
        else
        {
            mResponse = CharArrayToString(response);
        }

        //
        return result;
    }

    //
    // Get Request ...
    int GetRequest(
        const string url,       // Server Address
        const string headers,   // Headers providing
        const char &payload[],  // the Data which needs to Send
        char &response[],       // Response of request
        string responseHeaders, // Response Headers
        int timeout = 500       // Timeout for response default is 500
    )
    {
        //
        int result = SendRequest(
            X_HTTP_GET,
            url,
            headers,
            payload,
            response,
            responseHeaders,
            timeout);

        //
        return result;
    }

    //
    // Post Request ...
    int PostRequest(
        const string url,       // Server Address
        const string headers,   // Headers providing
        const char &payload[],  // the Data which needs to Send
        char &response[],       // Response of request
        string responseHeaders, // Response Headers
        int timeout = 500       // Timeout for response default is 500
    )
    {
        //
        int result = SendRequest(
            X_HTTP_POST,
            url,
            headers,
            payload,
            response,
            responseHeaders,
            timeout);

        //
        return result;
    }

    //
    // Handle Download Specific URL Content to Specific Path and File Name ...
    bool Download(
        string url,     // the URL address which going to download
        string fileName // Specify Destination file name to Store Response
    )
    {
        //
        bool result = false;

        //
        string filePath = GetFilePath(fileName);
        string cookie = NULL;
        string referer = NULL;
        int timeout = Timeout();

        //
        char payload[];
        string headers;
        char response[];
        string responseHeaders;

        //
        // Send Request ...
        int requestResult = GetRequest(
            url,
            headers,
            payload,
            response,
            responseHeaders,
            timeout);

        //
        // Define File Handler ...
        int mFileHandler = FileOpen(
            filePath,
            FILE_WRITE | FILE_BIN);
        if (mFileHandler == INVALID_HANDLE)
        {
            //
            mError = GetLastError();
            return result;
        }

        //
        // Write Response to File ...
        uint writed = FileWriteArray(
            mFileHandler,
            response,
            0,
            ArraySize(response));
        FileFlush(mFileHandler);
        FileClose(mFileHandler);

        //
        result = writed > 0;

        //
        return result;
    }

    //
    // END Provided Functions ...
    //

    //
    // Protected ...
  protected:
    //
    // Destintion Folder Path ...
    string mPath;

    //
    // WEB Request Timeout Value ...
    int mTimeout;

    //
    // WEB Request's Response ...
    string mResponse;

    //
    // Error Value ...
    int mError;

    //
    // Private ...
  private:
    //
    // Reset Errors State ...
    void ResetState()
    {
        //
        // Reset Errors ...
        mError = -1;
        mResponse = "";
        ResetLastError();
    }

    //
    // Generate Full File Path ...
    string GetFilePath(string fileName)
    {
        //
        string result = "";

        //
        result =
            //
            Path() + "\\" + fileName
            //
            ;

        //
        return result;
    }
};

//
// Tools ...

//
// Convert enum to String ...
string ToString(X_HTTP_METHOD method)
{
    //
    string result = "";

    //
    switch (method)
    {
    //
    case X_HTTP_GET:
        result = "GET";
        break;

    //
    case X_HTTP_POST:
        result = "POST";
        break;
    }

    //
    return result;
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-market.pattern.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XCMarketPatternDetector
// Description: provides all Alerts requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-bar.analyser.class.mq5"
#include "../Libraries/x-saherelm.x-pivot.analysis.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"

//
// Definitions ...

//
// Implementation ...
class XCMarketPatternDetector : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructor ...
    XCMarketPatternDetector()
    {
        barAnalyser = new XCBarAnalyser();
    }

    //
    // Deconstructor ...
    ~XCMarketPatternDetector()
    {
        //
        delete barAnalyser;
        ZeroMemory(barAnalyser);
    }

    //
    // Analysers ...

    /**
     * Check for Price Touch Support Level ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  supportBar: XOHCL, reference to holds Zone Bar ...
     * @param  supportBox: XBoxZone, reference to holds Zone Box ...
     * @param  validationLength: int, Validation Length ...
     * @param  maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
     *
     * @return ( bool )
     */
    bool IsSupportTouched(
        XOHCL &bar,
        XOHCL &supportBar,
        XBoxZone &supportBox,
        int validationLength = 21,
        int maxAllowedLoopback = 1500 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        supportBar.Clean();
        supportBox.Clean();

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Support Exists ...
        result = HasSupport(
            bar,
            supportBar,
            validationLength,
            maxAllowedLoopback //
        );
        if (!result)
        {
            //
            supportBar.Clean();
            return result;
        }

        //
        // Extract Support Box ...
        result = ToSupportBox(
            supportBar,
            supportBox //
        );

        //
        // Retrieve Previous Bar ...
        XOHCL pBar;
        result =
            result &&
            bar.GetPreviousBar(pBar);

        //
        // Chekc Price of Bar ...
        result =
            result &&
            pBar.low < supportBox.upper;

        //
        // Cleanup Resources ...
        if (!result)
        {
            //
            supportBar.Clean();
            supportBox.Clean();
        }
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check for Price Touch Resistance Level ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  supportBar: XOHCL, reference to holds Zone Bar ...
     * @param  supportBox: XBoxZone, reference to holds Zone Box ...
     * @param  validationLength: int, Validation Length ...
     * @param  maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
     *
     * @return ( bool )
     */
    bool IsResistanceTouched(
        XOHCL &bar,
        XOHCL &resistanceBar,
        XBoxZone &resistanceBox,
        int validationLength = 21,
        int maxAllowedLoopback = 1500 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        resistanceBar.Clean();
        resistanceBox.Clean();

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Resistance Exists ...
        result = HasResistance(
            bar,
            resistanceBar,
            validationLength,
            maxAllowedLoopback //
        );
        if (!result)
        {
            //
            resistanceBar.Clean();
            return result;
        }

        //
        // Extract Resistance Box ...
        result = ToResistanceBox(
            resistanceBar,
            resistanceBox //
        );

        //
        // Retrieve Previous Bar ...
        XOHCL pBar;
        result =
            result &&
            bar.GetPreviousBar(pBar);

        //
        // Chekc Price of Bar ...
        result =
            result &&
            pBar.high > resistanceBox.lower;

        //
        // Cleanup Resources ...
        if (!result)
        {
            //
            resistanceBar.Clean();
            resistanceBox.Clean();
        }
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check a Bar has Same Sequence Directionalo Bar or Not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, Detected Bars Direction ...
     * @param  count: int, Number of Same Bars ...
     * @param  validation: int, Validation of Same Bars ...
     * @param  loopback: int, Loopback for Detection ...
     *
     * @return ( bool )
     */
    bool HasSameBars(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int &count,
        int validation = 3 //
    )
    {
        //
        bool result = false;

        //
        // Normalize ...
        count = 0;
        dir = X_DIRECTION_NONE;

        //
        // Validate ...
        result =
            bar.IsValid() &&
            validation > 1;
        if (!result)
        {
            return result;
        }

        //
        XOHCL tmpBar;
        bar.GetPreviousBar(tmpBar);
        while ((tmpBar.GetDirection() == bar.GetDirection()) ||
               ((bar.IsBullish() && !tmpBar.IsBearish()) ||
                (bar.IsBearish() && !tmpBar.IsBullish())))
        {
            //
            count++;
            tmpBar.GetPreviousBar(tmpBar);
        }

        //
        // Validate Count ...
        result = count >= validation;
        if (result)
        {
            dir = bar.GetDirection();
        }

        //
        result =
            result &&
            count > 0 &&
            HasDirection(dir) &&
            count >= validation;

        //
        tmpBar.Clean();

        //
        return result;
    }

    /**
     * Detect Confluence Statck based on Given Bar ...
     *
     * @param  bar: XOHCL, Specified Bar for Start Detection ...
     * @param  zone: XBoxZone, confluence Zone, if exists ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Required Zone Confluence ...
     * @param  retests: int, Number of Zone retesting ...
     * @param  loopback: int, Max allowed Loopback for Confluence Detection ...
     * @param  rangeLength: int, Loopback for Range Detecttion ...
     * @param  upperPriceType: ENUM_X_PRICE, zone upper Price Type ...
     * @param  lowerPriceType: ENUM_X_PRICE,
     * @return ( bool )
     */
    bool HasConfluence(
        XOHCL &bar,
        XBoxZone &confluence,
        ENUM_X_DIRECTION forDir,
        int retests = 2,
        int loopback = 15,
        int rangeLength = 5,
        ENUM_X_PRICE upperPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE lowerPriceType = X_PRICE_LOW //
    )
    {
        //
        bool result = false;

        //
        // Normalize ...
        confluence.Clean();
        if (retests < 0)
        {
            retests = 0;
        }
        if (loopback < 2)
        {
            loopback = 2;
        }
        if (rangeLength < 2)
        {
            rangeLength = 2;
        }

        //
        // Validate ...
        result =
            bar.IsValid() &&
            HasDirection(forDir) &&
            IsXValid(upperPriceType) &&
            IsXValid(lowerPriceType);
        if (!result)
        {
            return result;
        }

        //
        // Retrieve Upper and Lower ...
        int mIndex = -1;
        double upper = bar.FindHighest(
            mIndex,
            rangeLength,
            upperPriceType //
        );
        double lower = bar.FindLowest(
            mIndex,
            rangeLength,
            lowerPriceType //
        );

        //
        // Validate Upper/Lower ...
        result =
            NotEmptyZero(upper) &&
            NotEmptyZero(lower) &&
            upper > lower;
        if (!result)
        {
            return result;
        }

        //
        // Validate Confluence ...
        XOHCL tmpBar;
        bool has = false;
        int tmpCount = 0;
        double tmpPrice = EMPTY_VALUE;
        for (int i = bar.Index() + 1; i < bar.Index() + loopback; i++)
        {
            //
            has = tmpBar.Init(bar.symbol, bar.period, i);
            if (!has)
            {
                continue;
            }

            //
            tmpPrice =
                IsXBullish(forDir)
                    ? tmpBar.GetPrice(upperPriceType)
                    : tmpBar.GetPrice(lowerPriceType);
            has =
                NotEmptyZero(tmpPrice) &&
                (IsXBullish(forDir)
                     ? tmpPrice < upper
                     : tmpPrice > lower);
            if (!has)
            {
                //
                tmpBar.Clean();

                //
                result = false;
                break;
            }

            //
            // Counting retests ...
            has = IsXBullish(forDir)
                      ? tmpPrice > lower
                      : tmpPrice > upper;
            if (has)
            {
                tmpCount++;
            }

            //
            tmpBar.Clean();
        }

        //
        // Validating Retests ...
        if (result && retests > 0)
        {
            //
            result =
                result &&
                tmpCount >= retests;
        }

        //
        if (!result)
        {
            return result;
        }

        //
        result = confluence.Init(
            bar.symbol,
            bar.period,
            upper,
            lower,
            GetBarTime(bar.symbol, bar.period, bar.Index() + loopback + 1),
            bar.time,
            forDir,
            "XConfluence",
            NULL // At ...
        );

        //
        return result;
    }

    //
    // Detectors ...

    /**
     * Check Specified Bar is Peak or Vale ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  validationLength: int, Validation Length ...
     *
     * @return ( bool )
     */
    bool IsPV(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int validationLength = 7,
        ENUM_X_PRICE peakPriceType = X_PRICE_HIGH, //
        ENUM_X_PRICE valePriceType = X_PRICE_LOW   //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalize ...
        validationLength = NormalizeInt(validationLength, 2);

        //
        // Validate ...
        result = bar.IsValid() &&
                 IsXValid(peakPriceType) &&
                 IsXValid(valePriceType);
        if (!result)
        {
            return result;
        }

        //
        int valeIndex = -1;
        double vale = bar.FindLowest(
            valeIndex,
            validationLength,
            valePriceType //
        );
        double barVale = bar.GetPrice(valePriceType);
        bool isVale =
            NotEmptyZero(vale) &&
            NotEmptyZero(barVale) &&
            IsValidIndex(valeIndex) &&
            vale >= barVale;

        //
        int peakIndex = -1;
        double peak = bar.FindHighest(
            peakIndex,
            validationLength,
            peakPriceType //
        );
        double barPeak = bar.GetPrice(peakPriceType);
        bool isPeak =
            NotEmptyZero(peak) &&
            NotEmptyZero(barPeak) &&
            IsValidIndex(peakIndex) &&
            peak <= barPeak;

        //
        result = (isVale && !isPeak) ||
                 (isPeak && !isVale);
        if (result)
        {
            //
            dir =
                isVale
                    ? X_DIRECTION_BULLISH
                : isPeak
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
        }

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...

        //
        return result;
    }

    /**
     * Check Specified Bar is Momentum Bar or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  requiredBars: int, bar range for approve Momentum ...
     * @param  approvedMultiplier: double, a Multipler for approve Momentum ...
     *
     * @return ( bool )
     */
    bool IsMomentumBar(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int requiredBars = 3,
        double approvedMultiplier = 2 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalizing ...
        requiredBars = NormalizeInt(requiredBars, 3, 10);
        approvedMultiplier = NormalizeDouble(approvedMultiplier, 2, 5);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Calculate Requirements ...

        //
        XOHCL iBar;
        bool has = false;
        double rangeBody = 0;
        int start = bar.Index() + 1;
        int end = start + requiredBars;
        for (int i = start; i < end; i++)
        {
            //
            // Initialized Bar ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );

            //
            // Summarize Body Sizes ...
            if (has)
            {
                rangeBody += iBar.GetBody();
            }

            //
            iBar.Clean();
        }

        //
        // Calculate Body Approvement ...
        result = bar.GetBody() >= (approvedMultiplier * rangeBody);
        if (!result)
        {
            return result;
        }

        //
        // Check Direction ...
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Engulfed Bar or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     *
     * @return ( bool )
     */
    bool IsEngulfedBar(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Calculate Requirements ...

        //
        XOHCL iBar;
        result = bar.GetPreviousBar(iBar);
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        // Check Prev Bar Must Inside Bar ...
        result = bar.GetUp() > iBar.GetUp() &&
                 bar.GetDown() < iBar.GetDown();
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            iBar.IsBearish() &&
            bar.IsBullish();
        bool isBearish =
            iBar.IsBullish() &&
            bar.IsBearish();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is True Gapped Bar or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  approvedPointMultiplier: double, Gap Validation Multiplier in Point ...
     *
     * @return ( bool )
     */
    bool IsTrueGapedBar(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double approvedPointMultiplier = 1 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalizing ...
        approvedPointMultiplier = NormalizeDouble(approvedPointMultiplier, 1, 100);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Calculate Requirements ...

        //
        XOHCL iBar;
        result = bar.GetPreviousBar(iBar);
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            iBar.IsBullish() &&
            bar.IsBullish();
        bool isBearish =
            iBar.IsBearish() &&
            bar.IsBearish();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        double point = GetPoints(bar.symbol);
        double approvedGapSize = approvedPointMultiplier * point;

        //
        // Check Gap Exists ...
        result =
            isBullish
                ? bar.low > iBar.high
                : iBar.low > bar.high;
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        // Validate Gap Size ...
        double gapSize = isBullish
                             ? bar.low - iBar.high
                             : iBar.low - bar.high;
        result = gapSize >= approvedGapSize;
        if (!result)
        {
            //
            iBar.Clean();
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar is Pinned Bar or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
     * @param  minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
     * @param  forceDirection: bool, Apply Force Bar Type ...
     *
     * @return ( bool )
     */
    bool IsPinnedBar(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double shadowApprovedMultiplier = 3,
        double minimumPinBarRangeMultiplier = 4,
        bool forceDirection = false //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalizing ...
        shadowApprovedMultiplier = NormalizeDouble(shadowApprovedMultiplier, 2, 5);
        minimumPinBarRangeMultiplier = NormalizeDouble(minimumPinBarRangeMultiplier, 2, 100);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Calculate Requirements ...
        double body = bar.GetBody();
        double lowShadow = bar.GetLowShadow();
        double highShadow = bar.GetHighShadow();

        //
        // Check Directions ...
        bool isBullish =
            lowShadow > body &&
            lowShadow > highShadow &&
            lowShadow >= (body * shadowApprovedMultiplier);
        bool isBearish =
            highShadow > body &&
            highShadow > lowShadow &&
            highShadow > (body * shadowApprovedMultiplier);
        result = isBullish || isBearish;
        if (!result)
        {
            return result;
        }

        //
        // Checking Force Bar Type ...
        if (forceDirection)
        {
            //
            result =
                isBullish
                    ? bar.IsBullish()
                    : bar.IsBearish();
        }
        if (!result)
        {
            return result;
        }

        //
        // Check Range Validation ...
        double point = GetPoints(bar.symbol);
        double approvedRange = minimumPinBarRangeMultiplier * point;
        result = bar.GetRange() >= approvedRange;
        if (!result)
        {
            return result;
        }

        //
        // Setting Direction ...
        dir =
            isBullish
                ? X_DIRECTION_BULLISH
                : X_DIRECTION_BEARISH;

        //
        result = HasDirection(dir);

        //
        return result;
    }

    /**
     * Check Specified Bar has Star Pattern or not ...
     * Morning Star => Bullish
     * Evening Star => Bearish
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     *
     * @return ( bool )
     */
    bool IsStarPattern(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Get Requirements ...
        XOHCL pBar, p2Bar;
        result = bar.GetPreviousBar(pBar);
        result = result &&
                 pBar.GetPreviousBar(p2Bar);
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            bar.IsBullish() &&
            p2Bar.IsBearish();
        bool isBearish =
            bar.IsBearish() &&
            p2Bar.IsBullish();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        // Checking Gap Exists Between Bars ...
        result =
            isBullish
                ? p2Bar.GetDown() > pBar.GetUp() &&
                      pBar.GetUp() < bar.GetDown()
                : p2Bar.GetUp() < pBar.GetDown() &&
                      pBar.GetDown() > bar.GetUp();
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        // Check Bar Close Validation ...
        double barClosedApprovedPrice = p2Bar.GetDown() + ((p2Bar.GetUp() - p2Bar.GetDown()) / 2);
        result =
            isBullish
                ? bar.close > barClosedApprovedPrice
                : bar.close < barClosedApprovedPrice;
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        // Validating PBar Body and Shadow ...
        double body = pBar.GetBody();
        double lowShadow = pBar.GetLowShadow();
        double highShadow = pBar.GetHighShadow();
        result =
            body < bar.GetBody() &&
            body < p2Bar.GetBody() &&
            (isBullish
                 ? lowShadow > body &&
                       lowShadow > highShadow
                 : highShadow > body &&
                       highShadow > lowShadow);
        if (!result)
        {
            //
            pBar.Clean();
            p2Bar.Clean();

            //
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        pBar.Clean();
        p2Bar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar has Pirecing Pattern or not ...
     * Piercing => Bullish
     * Dark Cloud Cover => Bearish
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     *
     * @return ( bool )
     */
    bool IsPiercingPattern(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            bar.IsBullish() &&
            pBar.IsBearish();
        bool isBearish =
            bar.IsBearish() &&
            pBar.IsBullish();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Check Bar Close ...
        double barCloseApprovedPrice = pBar.GetDown() + ((pBar.GetUp() - pBar.GetDown()) / 2);
        result =
            isBullish
                ? bar.close > barCloseApprovedPrice
                : bar.close < barCloseApprovedPrice;
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar has Rising Pattern or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     *
     * @return ( bool )
     */
    bool IsRisingPattern(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Reading Required Bars ...
        XOHCL iBar;
        XOHCL bars[];
        bool has = false;
        int start = bar.Index();
        int end = start + 5;
        for (int i = start; i < end; i++)
        {
            //
            // Initialized Bar ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );
            if (has)
            {
                //
                AddRef(
                    iBar,
                    bars //
                );
            }

            //
            iBar.Clean();
        }
        result = ArraySize(bars) == 5;
        if (!result)
        {
            //
            iBar.Clean();
            SpecifiedClean(bars);

            //
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            bars[0].IsBullish() &&
            bars[4].IsBullish() &&
            bars[1].IsBearish() &&
            bars[2].IsBearish() &&
            bars[3].IsBearish();
        bool isBearish =
            bars[0].IsBearish() &&
            bars[4].IsBearish() &&
            bars[1].IsBullish() &&
            bars[2].IsBullish() &&
            bars[3].IsBullish();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            iBar.Clean();
            SpecifiedClean(bars);

            //
            return result;
        }

        //
        // Check Range Validation ...
        double upper = bars[4].GetUp();
        double lower = bars[4].GetDown();

        //
        double max = MathMax(bars[1].GetUp(), bars[2].GetUp());
        max = MathMax(max, bars[3].GetUp());

        //
        double min = MathMin(bars[1].GetDown(), bars[2].GetDown());
        min = MathMin(min, bars[3].GetDown());

        //
        result =
            max < upper &&
            min > lower &&
            (isBullish
                 ? bars[0].close > bars[4].GetUp()
                 : bars[0].close < bars[4].GetDown());
        if (!result)
        {
            //
            iBar.Clean();
            SpecifiedClean(bars);

            //
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        iBar.Clean();
        SpecifiedClean(bars);

        //
        return result;
    }

    /**
     * Check Specified Bar has Flag Pattern or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  swingBar: XOHCL, Detected Swing Bar ...
     * @param  minimumPullbackBars: int, Minimum Shoulders of Swing Bar ...
     *
     * @return ( bool )
     */
    bool IsFlagPattern(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XOHCL &swingBar,
        int minimumPullbackBars = 3 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        swingBar.Clean();
        CleanDirection(dir);

        //
        // Normalizing ...
        minimumPullbackBars = NormalizeInt(minimumPullbackBars, 3, 10);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Reading Previous Bar ...
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Check Directions ...
        bool isBullish =
            bar.IsBullish() &&
            bar.close > pBar.GetUp();
        bool isBearish =
            bar.IsBearish() &&
            bar.close < pBar.GetDown();
        result = isBullish || isBearish;
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Detect Swing Bar ...
        ENUM_X_SWING_TYPE swingType =
            isBullish
                ? X_SWING_HIGH
                : X_SWING_LOW;
        result =
            result &&
            IsXValid(swingType) &&
            bar.FindNextSwingBar(
                swingType,
                swingBar,
                minimumPullbackBars //
                ) &&
            (isBullish
                 ? swingBar.IsBullish() &&
                       swingBar.high > pBar.high
                 : swingBar.IsBearish() &&
                       swingBar.low < pBar.low);
        if (!result)
        {
            //
            pBar.Clean();
            swingBar.Clean();

            //
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        // Get Result ...
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        pBar.Clean();
        if (!result)
        {
            swingBar.Clean();
        }

        //
        return result;
    }

    /**
     * Check Specified Bar has SignalKey Bar Pattern or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
     * @param  minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
     * @param  forceDirection: bool, Apply Force Bar Type ...
     *
     * @return ( bool )
     */
    bool IsSignalKeyBarPattern(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double shadowApprovedMultiplier = 3,
        double minimumPinBarRangeMultiplier = 4,
        bool forceDirection = false //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Get Requirements ...
        XOHCL pBar;
        result = bar.GetPreviousBar(pBar);
        if (!result)
        {
            return result;
        }

        //
        // Chck Previous Bar must Pinned ...
        result = IsPinnedBar(
            pBar,
            dir,
            shadowApprovedMultiplier,
            minimumPinBarRangeMultiplier,
            forceDirection //
        );
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        // Check Direction ...
        bool isBullish =
            bar.IsBullish() &&
            IsSpecifiedBullish(dir) &&
            bar.close > pBar.high;
        bool isBearish =
            bar.IsBearish() &&
            IsSpecifiedBearish(dir) &&
            bar.close < pBar.low;
        result = isBullish || isBearish;
        if (!result)
        {
            //
            pBar.Clean();
            return result;
        }

        //
        dir = bar.GetDirection();

        //
        result = HasDirection(dir);

        //
        // Cleanup Resources ...
        pBar.Clean();

        //
        return result;
    }

    /**
     * Check Specified Bar has Pullback Bar Pattern or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  zone: XBoxZone, refrence to hold detected zone ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  pullbackLength: int, min required indirectional bars to confirm pullback ...
     * @param  forceBreak: bool, force pullback bar breaks peak or vale of Zone ...
     *
     * @return ( bool )
     */
    bool IsPullbackPattern(
        XOHCL &bar,
        XBoxZone &zone,
        ENUM_X_DIRECTION &dir,
        int pullbackLength = 2,
        bool forceBreak = false //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        zone.Clean();
        CleanDirection(dir);

        //
        // Normalize ...
        pullbackLength = NormalizeInt(pullbackLength, 2);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // First Bar Direction ...
        dir = bar.GetDirection();
        result = HasDirection(dir);
        if (!result)
        {
            return result;
        }

        //
        // Loop through Pullback Length ...
        XOHCL iBar;
        datetime from = NULL;
        double max = EMPTY_VALUE;
        double min = EMPTY_VALUE;
        int start = bar.Index() + 1;
        int end = start + pullbackLength;
        for (int i = start; i < end; i++)
        {
            //
            // Initialize Indexed Bar ...
            iBar.Clean();
            result = bar.BarAt(i, iBar) &&
                     iBar.GetDirection() == Opposit(dir);
            if (!result)
            {
                break;
            }

            //
            // Calculate Max and Min ...
            max =
                !NotEmptyZero(max)
                    ? iBar.high
                    : MathMax(max, iBar.high);
            min =
                !NotEmptyZero(min)
                    ? iBar.low
                    : MathMin(min, iBar.low);
            from = iBar.time;
        }

        //
        // Apply Force Break ...
        if (result &&
            forceBreak)
        {
            //
            result =
                result &&
                NotEmptyZero(min) &&
                NotEmptyZero(max) &&
                (IsXBullish(dir)
                     ? bar.close > max
                     : bar.close < min);
        }

        //
        // Prepare Zone ...
        if (result)
        {
            //
            ToBox(
                bar,
                zone,
                dir,
                ToXString(X_BAR_PATTERN_PULLBACK) //
            );
            zone.upper = max;
            zone.lower = min;
            zone.from = from;
            zone.at = bar.time;
            zone.to = bar.time;

            //
            result = zone.IsValid();
        }

        //
        // Cleanup ...
        if (!result)
        {
            //
            zone.Clean();
            CleanDirection(dir);
        }
        iBar.Clean();

        //
        return result;
    }

    /**
     * Check Market Has Consolidation Pattern or not ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  zone: XBoxZone, refrence to hold detected zone ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Detected Order Flow Direction ...
     * @param  validationLength: int, Validation Length ...
     * @param  breakPriceType: ENUM_X_PRICE, Specified Price Type to Breake Consolidation ...
     * @param  upperPriceType: ENUM_X_PRICE, Specified Price Type to Detected Upper of Consolidation ...
     * @param  lowerPriceType: ENUM_X_PRICE, Specified Price Type to Detected Lower of Consolidation ...
     *
     * @return ( bool )
     */
    bool IsConsolidationPattern(
        XOHCL &bar,
        XBoxZone &zone,
        ENUM_X_DIRECTION &dir,
        int validationLength = 5,
        ENUM_X_PRICE breakPriceType = X_PRICE_CLOSE,
        ENUM_X_PRICE upperPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE lowerPriceType = X_PRICE_LOW //
    )
    {
        //
        // Descriptions:
        // ===============
        // Consolidation Zones Happens by Highest High and Lowest Low
        // of Specified Range which Breakes From one Side ...
        //

        //
        bool result = false;

        //
        // Prepare ...
        zone.Clean();
        CleanDirection(dir);

        //
        // Normalize ...
        validationLength = NormalizeInt(validationLength, 3);

        //
        // Validate ...
        result = bar.IsValid() &&
                 IsXValid(lowerPriceType) &&
                 IsXValid(upperPriceType) &&
                 IsXValid(breakPriceType);
        if (!result)
        {
            return result;
        }

        //
        // Define Requirements ...
        XOHCL iBar;
        bool has = false;
        datetime to = NULL;
        datetime from = NULL;
        double iLow = EMPTY_VALUE;
        bool hasLowShadow = false;
        double lower = EMPTY_VALUE;
        double upper = EMPTY_VALUE;
        double iHigh = EMPTY_VALUE;
        bool hasHighShadow = false;
        double iPrice = EMPTY_VALUE;
        bool isLowerBreaked = false;
        bool isUpperBreaked = false;

        //
        // Loop Through Bars ...
        int barIndex = bar.Index();
        int idx = barIndex;
        bool canLookup = IsValidIndex(idx);
        while (canLookup)
        {
            //
            // Initialize Indexed Bar ...
            idx++;
            iBar.Clean();
            result = bar.BarAt(idx, iBar);
            if (!result)
            {
                break;
            }

            //
            // Update Time ...
            if (idx == barIndex + 1)
            {
                to = iBar.time;
            }

            //
            // Extract Low and High Price ...
            iLow = iBar.GetPrice(lowerPriceType);
            iHigh = iBar.GetPrice(upperPriceType);
            result = NotEmptyZero(iLow) &&
                     NotEmptyZero(iHigh);
            if (!result)
            {
                break;
            }

            //
            // Check Bar Has Low/High Shadow ...
            hasLowShadow = iBar.GetLowShadow() > iBar.GetHighShadow();
            hasHighShadow = iBar.GetHighShadow() > iBar.GetLowShadow();

            //
            // Update Range Lower and Upper based on Shadows ...

            //
            // Lower ...
            if (hasLowShadow)
            {
                //
                // Check Can Update ...
                has =
                    !NotEmptyZero(lower)
                        ? true
                        : iLow < lower;
                if (has)
                {
                    lower = iLow;
                }
            }

            //
            // Upper ...
            if (hasHighShadow)
            {
                //
                // Check Can Update ...
                has =
                    !NotEmptyZero(upper)
                        ? true
                        : iHigh > upper;
                if (has)
                {
                    upper = iHigh;
                }
            }

            //
            // Check Price Breakes ...
            iPrice = iBar.GetPrice(breakPriceType);
            result = NotEmptyZero(iPrice);
            if (!result)
            {
                break;
            }

            //
            // Check Upper/Lower breakes ...
            isLowerBreaked = iPrice < lower;
            isUpperBreaked = iPrice > upper;

            //
            canLookup = (!isLowerBreaked && !isUpperBreaked);
            if (!canLookup)
            {
                break;
            }

            //
            from = iBar.time;
        }

        //
        // Validate Calculations ...
        result =
            //
            IsXValid(to) &&
            IsXValid(from) &&
            from < to &&
            //
            NotEmptyZero(lower) &&
            NotEmptyZero(upper) &&
            upper > lower
            //
            ;

        //
        // Validate To Edge Breakes ...
        if (result)
        {
            //
            iPrice = bar.GetPrice(breakPriceType);
            result = NotEmptyZero(iPrice);

            //
            // Validate To Edge Breakes ...
            if (result)
            {
                //
                isUpperBreaked = iPrice > upper;
                isLowerBreaked = iPrice < lower;

                //
                // Validate To Break ...
                result = isUpperBreaked || isLowerBreaked;
            }

            //
            // Prepare Direction ...
            if (result)
            {
                //
                dir =
                    isUpperBreaked
                        ? X_DIRECTION_BULLISH
                        : X_DIRECTION_BEARISH;
            }

            //
            // Validate Direction ...
            result = HasDirection(dir);
        }

        //
        // Prepare Zone ...
        if (result)
        {
            //
            zone.to = to;
            zone.dir = dir;
            zone.from = from;
            zone.at = bar.time;
            zone.upper = upper;
            zone.lower = lower;
            zone.symbol = bar.symbol;
            zone.period = bar.period;
            zone.type = ToXString(X_BAR_PATTERN_CONSOLIDATION);

            //
            // Validate Zone ...
            result = zone.IsValid();

            //
            // Validate Zone Length ...
            if (result)
            {
                result = zone.FromIndex() - zone.ToIndex() >= validationLength;
            }
        }

        //
        // Cleanup ...
        if (!result)
        {
            //
            zone.Clean();
            CleanDirection(dir);
        }
        iBar.Clean();

        //
        return result;
    }

    bool HasCandlestickDirection(
        XOHCL &bar,
        ENUM_X_DIRECTION forDir,
        bool checkStarPattern = true,
        bool checkPinBarPattern = true,
        bool checkPiercingPattern = true,
        bool checkEngulfedPattern = true,
        bool checkMomentumPattern = true,
        bool checkSignalKeyBarPattern = true //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result = bar.IsValid() &&
                 HasDirection(forDir);
        if (!result)
        {
            return result;
        }

        //
        ENUM_X_DIRECTION tmpDir = X_DIRECTION_NONE;

        //
        // Star ...
        bool isStarPattern = false;
        if (checkStarPattern)
        {
            //
            isStarPattern =
                IsStarPattern(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        bool isPiercingPattern = false;
        if (checkPiercingPattern)
        {
            //
            isPiercingPattern =
                IsPiercingPattern(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        // Engulfed ...
        bool isEngulfedPattern = false;
        if (checkEngulfedPattern)
        {
            //
            isEngulfedPattern =
                IsEngulfedBar(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        // PinBar ...
        bool isPinBarPattern = true;
        if (checkPinBarPattern)
        {
            //
            isPinBarPattern =
                IsPinnedBar(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        // Signal Key Bar ...
        bool isSignalKeyBarPattern = false;
        if (checkSignalKeyBarPattern)
        {
            //
            isSignalKeyBarPattern =
                IsSignalKeyBarPattern(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        // Momentum Bar ...
        bool isMomentumBarPattern = false;
        if (checkMomentumPattern)
        {
            //
            isMomentumBarPattern =
                IsMomentumBar(bar, tmpDir) &&
                tmpDir == forDir;
        }

        //
        result =
            isStarPattern ||
            isPinBarPattern ||
            isPiercingPattern ||
            isEngulfedPattern ||
            isMomentumBarPattern ||
            isSignalKeyBarPattern;

        //
        return result;
    }

    /**
     * Detect Order Flow ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Detected Order Flow Direction ...
     * @param  orderFlow: XBoxZone, collection reference to holds Detected Order Flow ...
     * @param  orderFlowValidation: int, number of sequential Zones for Validate Order Flow ...
     * @param  containsOBs: bool, Specified to Use Order Blocks in Order Flow Detection ...
     * @param  containsFVGs: bool, Specified to Use Fair Value Gaps in Order Flow Detection ...
     * @param  containsSupportResistances: bool, Specified to Use Support and Resistances in Order Flow Detection ...
     * @param  zonesLoopback: int, Zone Detection Loopback ...
     * @param  zonesValidationLength: int, Zone Validation Length ...
     * @param  loopback: int, Max Allowed Loopback Bars for Collecting ...
     *
     * @return ( bool )
     */
    bool HasOrderFlow(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XBoxZone &orderFlow[],
        int orderFlowValidation = 2,
        bool containsOBs = false,
        bool containsFVGs = true,
        bool containsSupportResistances = false,
        int zonesLoopback = 5,
        int zonesValidationLength = 21,
        int loopback = 100 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        XClean(orderFlow);
        CleanDirection(dir);
        int maxRequiredZone = 0;

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 10);
        zonesLoopback = NormalizeInt(zonesLoopback, 3);
        maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
        orderFlowValidation = NormalizeInt(orderFlowValidation, 1);
        zonesValidationLength = NormalizeInt(zonesValidationLength, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Do ...

        //
        // Collectiong Zones ...
        XBoxZone tmp;
        XBoxZone tmps[];
        XBoxZone _zones[];
        XBoxZone bullishZones[];
        XBoxZone bearishZones[];
        int bullishZonesCount = 0;
        int bearishZonesCount = 0;

        //
        // Order Blocks ...
        if (containsOBs)
        {
            //
            XClean(tmps);
            CollectOBs(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
            FillNotExists(_zones, tmps);
        }

        //
        // Fair Value Gaps ...
        if (containsFVGs)
        {
            //
            XClean(tmps);
            CollectFVGs(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
            FillNotExists(_zones, tmps);
        }

        //
        // Support and Resistances ...
        if (containsSupportResistances)
        {
            //
            XClean(tmps);
            CollectZones(bar, tmps, zonesLoopback, maxRequiredZone, loopback);
            FillNotExists(_zones, tmps);
        }

        //
        // Validate Zones ...
        result = HasChild(_zones);

        //
        // Extract Only Validated Zones ...
        if (result)
        {
            //
            ValidateBoxes(
                _zones,
                bar.Index() // Bars Length ...
            );

            //
            UpdateToTime(_zones, bar.time);
            RemoveBreakedZones(_zones, X_PRICE_CLOSE);

            //
            result = HasChild(_zones);
        }

        //
        // Separate Zones Direction ...
        if (result)
        {
            //
            bullishZonesCount = ExtractByDirection(bullishZones, _zones, X_DIRECTION_BULLISH);
            bearishZonesCount = ExtractByDirection(bearishZones, _zones, X_DIRECTION_BEARISH);

            //
            result = IsValidSize(bullishZonesCount) ||
                     IsValidSize(bearishZonesCount);
        }

        //
        // Looking for Sequences of Zones ...
        if (result)
        {
            //
            int idx = -1;
            bool has = false;
            XBoxZone bullOrderFlow[];
            XBoxZone bearOrderFlow[];

            //
            // Bullish OrderFlow ...

            //
            // Sorting ...
            Copy(bullishZones, bullOrderFlow);
            Sort(
                bullOrderFlow,
                X_SORT_BY_DATE,
                X_DIRECTION_BEARISH,
                orderFlowValidation + 1 //
            );
            bool hasBullishOrderFlow = ArraySize(bullOrderFlow) >= orderFlowValidation;
            if (hasBullishOrderFlow)
            {
                //
                // Validate Order Flow ...
                Copy(bullOrderFlow, tmps);
                XClean(bullOrderFlow);
                while (HasChild(tmps))
                {
                    //
                    tmp.Clean();
                    tmp = tmps[0];
                    ArrayRemove(tmps, 0, 1);

                    //
                    idx = GetHighest(tmps);
                    has = !IsValidIndex(idx)
                              ? true
                              : tmps[idx].lower < tmp.lower;
                    if (has)
                    {
                        AddIfNotExists(tmp, bullOrderFlow);
                    }
                }

                //
                hasBullishOrderFlow = ArraySize(bullOrderFlow) >= orderFlowValidation;
            }

            //
            // Bearish OrderFlow ...

            //
            // Sorting ...
            Copy(bearishZones, bearOrderFlow);
            Sort(
                bearOrderFlow,
                X_SORT_BY_DATE,
                X_DIRECTION_BULLISH,
                orderFlowValidation + 1 //
            );
            bool hasBearishOrderFlow = ArraySize(bearOrderFlow) >= orderFlowValidation;
            if (hasBearishOrderFlow)
            {
                //
                // Validate Order Flow ...
                Copy(bearOrderFlow, tmps);
                XClean(bearOrderFlow);
                while (HasChild(tmps))
                {
                    //
                    tmp.Clean();
                    tmp = tmps[0];
                    ArrayRemove(tmps, 0, 1);

                    //
                    idx = GetLowest(tmps);
                    has = !IsValidIndex(idx)
                              ? true
                              : tmps[idx].upper > tmp.upper;
                    if (has)
                    {
                        AddIfNotExists(tmp, bearOrderFlow);
                    }
                }

                //
                hasBearishOrderFlow = ArraySize(bearOrderFlow) >= orderFlowValidation;
            }

            //
            result = hasBullishOrderFlow ||
                     hasBearishOrderFlow;

            //
            // Validate Order Flows ...
            if (result)
            {
                //
                has = (hasBullishOrderFlow && !hasBearishOrderFlow) ||
                      (hasBearishOrderFlow && !hasBullishOrderFlow);

                //
                // When only Have One Direction ...
                if (has)
                {
                    //
                    if (hasBullishOrderFlow)
                    {
                        //
                        dir = X_DIRECTION_BULLISH;
                        Copy(bullOrderFlow, orderFlow);
                    }
                    else if (hasBearishOrderFlow)
                    {
                        //
                        dir = X_DIRECTION_BEARISH;
                        Copy(bearOrderFlow, orderFlow);
                    }
                }
                //
                // When Two Direction Exists ...
                else
                {
                    //
                    // Retrieve Oldest Of Bullish / Bearish ...
                    int oldestBullIDX = GetOldest(bullOrderFlow);
                    int oldestBearIDX = GetOldest(bearOrderFlow);
                    result = IsValidIndex(oldestBullIDX) &&
                             IsValidIndex(oldestBearIDX);
                    if (result)
                    {
                        //
                        dir =
                            bullOrderFlow[oldestBullIDX].from > bearOrderFlow[oldestBearIDX].from
                                ? X_DIRECTION_BULLISH
                            : bearOrderFlow[oldestBearIDX].from > bullOrderFlow[oldestBullIDX].from
                                ? X_DIRECTION_BEARISH
                                : X_DIRECTION_NONE;
                    }

                    //
                    result = HasDirection(dir);
                    if (result)
                    {
                        //
                        if (IsXBullish(dir))
                        {
                            Copy(bullOrderFlow, orderFlow);
                        }
                        else if (IsXBearish(dir))
                        {
                            Copy(bearOrderFlow, orderFlow);
                        }
                    }
                }
            }

            //
            XClean(bullOrderFlow);
            XClean(bearOrderFlow);

            //
            result = HasChild(orderFlow) &&
                     HasDirection(dir);
        }

        //
        // Validate Price ...
        if (result)
        {
            //
            int idx = -1;
            if (IsXBullish(dir))
            {
                //
                idx = GetHighest(orderFlow);
                result = IsValidIndex(idx) &&
                         bar.low > orderFlow[idx].upper;
            }
            else
            {
                //
                idx = GetLowest(orderFlow);
                result = IsValidIndex(idx) &&
                         bar.high < orderFlow[idx].lower;
            }
        }

        //
        // Update To Time of Order Flow ...
        if (result)
        {
            UpdateToTime(orderFlow, bar.time);
        }

        //
        // Cleanup ...
        if (!result)
        {
            //
            XClean(orderFlow);
            CleanDirection(dir);
        }
        tmp.Clean();
        XClean(tmps);
        XClean(_zones);
        XClean(bullishZones);
        XClean(bearishZones);

        //
        return result;
    }

    //
    // FVG ...

    /**
     * Check For FVG Exists in Specified Loopback Period ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  fvg: XBoxZone, reference for Holding FVG ...
     * @param  maxLoopback: int, Max Allowed Loopback to Lookup FVG ...
     *
     * @return ( bool )
     */
    bool HasFVG(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XBoxZone &fvg,
        int maxLoopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        fvg.Clean();
        CleanDirection(dir);

        //
        // Normalize ...
        maxLoopback = NormalizeInt(maxLoopback, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL iBar;
        bool has = false;
        int start = bar.Index();
        int end = start + maxLoopback;
        for (int i = start; i < end; i++)
        {
            //
            // Initialize iBar ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );
            if (!has)
            {
                continue;
            }

            //
            // Check iBar is FVG or not ...
            has = barAnalyser.IsFVG(
                iBar,
                fvg,
                true // Force Bar Type ...
            );
            if (!has)
            {
                //
                fvg.Clean();
                iBar.Clean();
                CleanDirection(dir);

                //
                continue;
            }

            //
            iBar.Clean();
            dir = fvg.dir;

            //
            break;
        }

        //
        // Check FVG Exists ...
        result = fvg.IsValid();

        //
        // Check Direction Exists ...
        result =
            result &&
            HasDirection(dir);

        //
        // Cleanup Resources ...
        if (!result)
        {
            //
            fvg.Clean();
            CleanDirection(dir);
        }
        iBar.Clean();

        //
        return result;
    }

    //
    // OB ...

    /**
     * Check For OB Exists in Specified Loopback Period ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  ob: XBoxZone, reference for Holding OB ...
     * @param  maxLoopback: int, Max Allowed Loopback to Lookup OB ...
     *
     * @return ( bool )
     */
    bool HasOB(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XBoxZone &ob,
        int maxLoopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        ob.Clean();
        CleanDirection(dir);

        //
        // Normalize ...
        maxLoopback = NormalizeInt(maxLoopback, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL iBar;
        bool has = false;
        int start = bar.Index();
        int end = start + maxLoopback;
        for (int i = start; i < end; i++)
        {
            //
            // Initialize iBar ...
            has = iBar.Init(
                bar.symbol,
                bar.period,
                i //
            );
            if (!has)
            {
                continue;
            }

            //
            // Check iBar is OB or not ...
            has = barAnalyser.IsOB(
                iBar,
                ob,
                true, // Force FVG Bar Type ...
                true  // Force OB First Two Bar InDirection ...
            );
            if (!has)
            {
                //
                ob.Clean();
                iBar.Clean();
                CleanDirection(dir);

                //
                continue;
            }

            //
            iBar.Clean();
            dir = ob.dir;

            //
            break;
        }

        //
        // Check OB Exists ...
        result = ob.IsValid();

        //
        // Check Direction Exists ...
        result =
            result &&
            HasDirection(dir);

        //
        // Cleanup Resources ...
        if (!result)
        {
            //
            ob.Clean();
            CleanDirection(dir);
        }
        iBar.Clean();

        //
        return result;
    }

    //
    // Complex ...

    /**
     * Check Double Pinned Bar ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
     * @param  shadowApprovedMultiplier: double, Shadow Size Approve Multiplier ...
     * @param  minimumPinBarRangeMultiplier: doubble, Range Size Approve Multiplier in Point ...
     * @param  forceDirection: bool, Apply Force Bar Type ...
     * @param  loopback: int, Max Allowed Detecting Loopback lenght ...
     *
     * @return ( bool )
     */
    bool IsDoublePinned(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        double shadowApprovedMultiplier = 3,
        double minimumPinBarRangeMultiplier = 4,
        bool forceDirection = false,
        int loopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 1);
        shadowApprovedMultiplier = NormalizeDouble(shadowApprovedMultiplier, 2, 5);
        minimumPinBarRangeMultiplier = NormalizeDouble(minimumPinBarRangeMultiplier, 2, 100);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Do ...

        //
        // Current Bar Must be a Pinned Bar ...
        result = IsPinnedBar(
            bar,
            dir,
            shadowApprovedMultiplier,
            minimumPinBarRangeMultiplier,
            forceDirection //
        );
        if (!result)
        {
            //
            CleanDirection(dir);

            //
            return result;
        }

        //
        XOHCL iBar;
        ENUM_X_DIRECTION iDir;
        int start = bar.Index() + 1;
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            result = bar.BarAt(i, iBar);
            result = result && IsPinnedBar(
                                   iBar,
                                   iDir,
                                   shadowApprovedMultiplier,
                                   minimumPinBarRangeMultiplier,
                                   forceDirection //
                               );
            result = result &&
                     iDir == dir;
            if (result)
            {
                break;
            }
        }

        //
        // Cleanup ...
        if (!result)
        {
            CleanDirection(dir);
        }
        iBar.Clean();
        CleanDirection(iDir);

        //
        return result;
    }

    /**
     * Check Double Engulfed Bar ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
     * @param  loopback: int, Max Allowed Detecting Loopback lenght ...
     *
     * @return ( bool )
     */
    bool IsDoubleEngulfed(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int loopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 1);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Do ...

        //
        // Current Bar Must be a Pinned Bar ...
        result = IsEngulfedBar(
            bar,
            dir //
        );
        if (!result)
        {
            //
            CleanDirection(dir);

            //
            return result;
        }

        //
        XOHCL iBar;
        ENUM_X_DIRECTION iDir;
        int start = bar.Index() + 1;
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            result = bar.BarAt(i, iBar);
            result = result && IsEngulfedBar(
                                   iBar,
                                   iDir //
                               );
            result = result &&
                     iDir == dir;
            if (result)
            {
                break;
            }
        }

        //
        // Cleanup ...
        if (!result)
        {
            CleanDirection(dir);
        }
        iBar.Clean();
        CleanDirection(iDir);

        //
        return result;
    }

    /**
     * Check Double Momentum Bar ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Pattern Direction ...
     * @param  requiredBars: int, bar range for approve Momentum ...
     * @param  approvedMultiplier: double, a Multipler for approve Momentum ...
     * @param  loopback: int, Max Allowed Detecting Loopback lenght ...
     *
     * @return ( bool )
     */
    bool IsDoubleMomentum(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        int requiredBars = 3,
        double approvedMultiplier = 2,
        int loopback = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 1);
        requiredBars = NormalizeInt(requiredBars, 3, 10);
        approvedMultiplier = NormalizeDouble(approvedMultiplier, 2, 5);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Do ...

        //
        // Current Bar Must be a Pinned Bar ...
        result = IsMomentumBar(
            bar,
            dir,
            requiredBars,
            approvedMultiplier //
        );
        if (!result)
        {
            //
            CleanDirection(dir);

            //
            return result;
        }

        //
        XOHCL iBar;
        ENUM_X_DIRECTION iDir;
        int start = bar.Index() + 1;
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            result = bar.BarAt(i, iBar);
            result = result && IsMomentumBar(
                                   iBar,
                                   iDir,
                                   requiredBars,
                                   approvedMultiplier //
                               );
            result = result &&
                     iDir == dir;
            if (result)
            {
                break;
            }
        }

        //
        // Cleanup ...
        if (!result)
        {
            CleanDirection(dir);
        }
        iBar.Clean();
        CleanDirection(iDir);

        //
        return result;
    }

    /**
     * Check Has Double Patterns ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION, reference to holds Specified Patterns ...
     * @param  barPatterns: XPatternAnalysis, reference to Provides Bar's Pattern Analysis ...
     * @param  pBarPatterns: XPatternAnalysis, reference to Provides Previous Bar's Pattern Analysis ...
     * @param  allowedPatterns: ENUM_X_BAR_PATTERN, collection to Provide Allowed Bar Patterns for Detection ...
     * @param  config: XPatternAnalysisConfig, Provides Patterns Detection Config ...
     * @param  loopback: int, Max Allowed Loopback for Detection ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Patterns Detection Direction ...
     *
     * @return ( bool )
     */
    bool HasDoublePatterns(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XPatternAnalysis &barPatterns,
        XPatternAnalysis &pBarPatterns,
        ENUM_X_BAR_PATTERN &allowedPatterns[],
        XPatternAnalysisConfig &config,
        int loopback = 5,
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        barPatterns.Clean();
        CleanDirection(dir);
        pBarPatterns.Clean();
        XPatternAnalysisConfig iConfig = config;

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 2);

        //
        // Validate ...
        result = bar.IsValid() &&
                 HasChild(allowedPatterns);
        if (!result)
        {
            return result;
        }

        //
        // Prepare Config ...
        iConfig.CleanPatterns();
        if (HasChild(allowedPatterns))
        {
            //
            for (int i = 0; i < ArraySize(allowedPatterns); i++)
            {
                iConfig.AddPattern(allowedPatterns[i]);
            }
        }

        //
        // Detect Bar Patterns ...
        result = AnalyseBarPatterns(bar, barPatterns, config);
        if (!result)
        {
            //
            CleanDirection(dir);
            barPatterns.Clean();
            pBarPatterns.Clean();

            //
            return result;
        }

        //
        // Check Direction ...
        int bullishCount = barPatterns.Count(X_DIRECTION_BULLISH);
        int bearishCount = barPatterns.Count(X_DIRECTION_BEARISH);
        dir =
            bullishCount > bearishCount
                ? X_DIRECTION_BULLISH
            : bearishCount > bullishCount
                ? X_DIRECTION_BEARISH
                : X_DIRECTION_NONE;
        result = HasDirection(dir);

        //
        // Check for Dir ...
        if (HasDirection(forDir))
        {
            //
            result =
                result &&
                dir == forDir;
        }

        //
        // Looping Back ...
        if (result)
        {
            //
            XOHCL iBar;
            ENUM_X_DIRECTION iDir;
            int start = bar.Index() + 1;
            int end = start + loopback;
            for (int i = start; i < end; i++)
            {
                //
                iBar.Clean();
                pBarPatterns.Clean();
                result = bar.BarAt(i, iBar);
                result = result &&
                         AnalyseBarPatterns(iBar, pBarPatterns, iConfig);
                if (!result)
                {
                    continue;
                }

                //
                bullishCount = pBarPatterns.Count(X_DIRECTION_BULLISH);
                bearishCount = pBarPatterns.Count(X_DIRECTION_BEARISH);
                iDir =
                    bullishCount > bearishCount
                        ? X_DIRECTION_BULLISH
                    : bearishCount > bullishCount
                        ? X_DIRECTION_BEARISH
                        : X_DIRECTION_NONE;
                result = HasDirection(iDir);
                result = result &&
                         dir == iDir;
                if (result)
                {
                    break;
                }
            }

            //
            iBar.Clean();
            CleanDirection(iDir);
        }

        //
        // Cleanup ...
        if (!result)
        {
            //
            CleanDirection(dir);
            barPatterns.Clean();
            pBarPatterns.Clean();
        }
        iConfig.Clean();

        //
        return result;
    }

    /**
     * Count Same Bars ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  sameBars: XOHCL, collection reference to holds same Bars ...
     * @param  dir: ENUM_X_DIRECTION, refrence to Founded Bars Direction holding ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Looking Bar ...
     *
     * @return ( int )
     */
    int CountSameBars(
        XOHCL &bar,
        XOHCL &sameBars[],
        ENUM_X_DIRECTION &dir,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(sameBars);

        //
        // Validate ...
        bool has = bar.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XOHCL tmpBar;
        ENUM_X_DIRECTION tmpDir = forDir;
        int idx = bar.Index();
        while (has)
        {
            //
            idx++;
            tmpBar.Clean();
            has = bar.BarAt(idx, tmpBar);
            if (has && !HasDirection(tmpDir))
            {
                tmpDir = tmpBar.GetDirection();
            }
            has = has &&
                  tmpBar.GetDirection() == tmpDir;
            if (!has)
            {
                break;
            }

            //
            AddIfNotExists(tmpBar, sameBars);
        }

        //
        result = ArraySize(sameBars);
        if (IsValidSize(result))
        {
            dir = tmpDir;
        }

        //
        // Cleanup ...
        tmpBar.Clean();

        //
        return result;
    }

    //
    // Trending ...

    /**
     * Check Trend Exists Based on Swings ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  dir: ENUM_X_DIRECTION a reference for Direction ...
     * @param  swings: XOHCL Array, Holds Swings ...
     * @param  numberOfSwings: int, Number of Swing Points for Detect Trend ...
     * @param  swingShoulders: int, Swing Shoulders Approvement Length ...
     *
     * @return ( bool )
     */
    bool HasSwingTrend(
        XOHCL &bar,
        ENUM_X_DIRECTION &dir,
        XOHCL &swings[],
        int numberOfSwings = 3,
        int swingShoulders = 7 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanDirection(dir);
        SpecifiedClean(swings);

        //
        // Normalizing ...
        numberOfSwings = NormalizeInt(numberOfSwings, 3, 10);
        swingShoulders = NormalizeInt(swingShoulders, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Trend Direction ...
        XOHCL iSwing;
        int idx = -1;
        bool has = false;
        XOHCL swingLows[];
        XOHCL swingHighs[];
        int lastSwingLowIndex = bar.Index();
        int lastSwingHighIndex = bar.Index();
        while (ArraySize(swingLows) < numberOfSwings || ArraySize(swingHighs) < numberOfSwings)
        {
            //
            bool canLookupSwingLow = ArraySize(swingLows) < numberOfSwings;
            bool canLookupSwingHigh = ArraySize(swingHighs) < numberOfSwings;
            if (!canLookupSwingLow && !canLookupSwingHigh)
            {
                break;
            }

            //
            // Handle Swing Lows ...
            if (canLookupSwingLow)
            {
                //
                iSwing.Clean();
                idx = FindXSwing(
                    X_SWING_LOW,
                    bar.symbol,
                    bar.period,
                    lastSwingLowIndex + 1,
                    swingShoulders //
                );
                has = IsValidIndex(idx);
                if (has)
                {
                    //
                    has = iSwing.Init(
                        bar.symbol,
                        bar.period,
                        idx //
                    );
                    if (has)
                    {
                        //
                        AddRef(
                            iSwing,
                            swingLows //
                        );

                        //
                        lastSwingLowIndex = idx;
                    }
                }
            }

            //
            // Handle Swing Highs ...
            if (canLookupSwingHigh)
            {
                //
                iSwing.Clean();
                idx = FindXSwing(
                    X_SWING_HIGH,
                    bar.symbol,
                    bar.period,
                    lastSwingHighIndex + 1,
                    swingShoulders //
                );
                has = IsValidIndex(idx);
                if (has)
                {
                    //
                    has = iSwing.Init(
                        bar.symbol,
                        bar.period,
                        idx //
                    );
                    if (has)
                    {
                        //
                        AddRef(
                            iSwing,
                            swingHighs //
                        );

                        //
                        lastSwingHighIndex = idx;
                    }
                }
            }
        }

        //
        // Check Trend Based On Swings ...

        //
        // Check Directions ...

        //
        bool isBullish = false;
        bool isBearish = false;

        //
        datetime oldestSwingLow = NULL;
        datetime oldestSwingHigh = NULL;

        //
        // Checking Bullish Trend based on Swing Lows ...
        int count = ArraySize(swingLows);
        for (int i = 0; i < count - 1; i++)
        {
            //
            has = swingLows[i].low > swingLows[i + 1].low;
            isBullish =
                !isBullish
                    ? has
                    : isBullish && has;
            if (!isBullish)
            {
                break;
            }

            //
            datetime oldeTime = MathMin(swingLows[i].time, swingLows[i + 1].time);
            oldestSwingLow =
                !IsSpecifiedValid(oldestSwingLow)
                    ? oldeTime
                    : MathMin(oldestSwingLow, oldeTime);
        }

        //
        // Checking Bearish Trend based on Swing Highs ...
        count = ArraySize(swingHighs);
        for (int i = 0; i < count - 1; i++)
        {
            //
            has = swingHighs[i].high < swingHighs[i + 1].high;
            isBearish =
                !isBearish
                    ? has
                    : isBearish && has;
            if (!isBearish)
            {
                break;
            }

            //
            datetime oldeTime = MathMin(swingHighs[i].time, swingHighs[i + 1].time);
            oldestSwingHigh =
                !IsSpecifiedValid(oldestSwingHigh)
                    ? oldeTime
                    : MathMin(oldestSwingHigh, oldeTime);
        }

        //
        result = isBullish || isBearish;
        if (!result)
        {
            //
            iSwing.Clean();
            SpecifiedClean(swingLows);
            SpecifiedClean(swingHighs);

            //
            return result;
        }

        //
        if (isBullish &&
            isBearish &&
            IsSpecifiedValid(oldestSwingLow) &&
            IsSpecifiedValid(oldestSwingHigh))
        {
            //
            // Here we Have to Detect Newest Trend or Oldest Trend ...
            isBullish = oldestSwingLow < oldestSwingHigh;
            isBearish = oldestSwingHigh < oldestSwingLow;
        }

        //
        result = (isBullish && !isBearish) ||
                 (isBearish && !isBullish);
        if (!result)
        {
            //
            iSwing.Clean();
            SpecifiedClean(swingLows);
            SpecifiedClean(swingHighs);

            //
            return result;
        }

        //
        if (isBullish)
        {
            //
            Copy(
                swingLows,
                swings //
            );

            //
            dir = X_DIRECTION_BULLISH;
        }
        else
        {
            //
            Copy(
                swingHighs,
                swings //
            );

            //
            dir = X_DIRECTION_BEARISH;
        }

        //
        iSwing.Clean();
        SpecifiedClean(swingLows);
        SpecifiedClean(swingHighs);

        //
        return result;
    }

    /**
     * Check Specified Bar Has Support or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  support: XOHCL, Supported Bar ...
     * @param  validationLength: int, Validation Length ...
     * @param  maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
     *
     * @return ( bool )
     */
    bool HasSupport(
        XOHCL &bar,
        XOHCL &support,
        int validationLength = 21,
        int maxAllowedLoopback = 1500 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        support.Clean();

        //
        // Normalizing ...
        validationLength = NormalizeInt(validationLength, 7);
        maxAllowedLoopback = NormalizeInt(maxAllowedLoopback, 21, 1500);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Find Support Swing ...
        int idx = -1;
        bool has = false;
        int lookupIDX = bar.Index();
        while (!IsValidIndex(idx) &&
               lookupIDX < maxAllowedLoopback)
        {
            //
            // Detect Swing ...
            idx = FindXSwing(
                X_SWING_LOW,
                bar.symbol,
                bar.period,
                lookupIDX,
                validationLength //
            );
            has = IsValidIndex(idx);
            if (!has)
            {
                //
                lookupIDX++;
                continue;
            }

            //
            // Retrieve Swing Bar ...
            has = support.Init(
                bar.symbol,
                bar.period,
                idx //
            );
            if (!has)
            {
                //
                idx = -1;
                lookupIDX++;
                support.Clean();
                continue;
            }

            //
            // Check Suppor Low Shadow ...
            has = support.GetDown() < bar.low;
            if (!has)
            {
                //
                idx = -1;
                lookupIDX++;
                support.Clean();
                continue;
            }

            //
            if (IsValidIndex(idx))
            {
                break;
            }
        }

        //
        result = support.IsValid();

        //
        return result;
    }

    /**
     * Check Specified Bar Has Resistance or not ...
     *
     * @param  bar: XOHCL, Specified Bar ...
     * @param  resistence: XOHCL, Resistanced Bar ...
     * @param  validationLength: int, Validation Length ...
     * @param  maxAllowedLoopback: int, Max Allowed Loopback to Detection ...
     *
     * @return ( bool )
     */
    bool HasResistance(
        XOHCL &bar,
        XOHCL &resistence,
        int validationLength = 21,
        int maxAllowedLoopback = 1500 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        resistence.Clean();

        //
        // Normalizing ...
        validationLength = NormalizeInt(validationLength, 7);
        maxAllowedLoopback = NormalizeInt(maxAllowedLoopback, 21, 1500);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Find Support Swing ...
        int idx = -1;
        bool has = false;
        int lookupIDX = bar.Index();
        while (!IsValidIndex(idx) &&
               lookupIDX < maxAllowedLoopback)
        {
            //
            // Detect Swing ...
            idx = FindXSwing(
                X_SWING_HIGH,
                bar.symbol,
                bar.period,
                lookupIDX,
                validationLength //
            );
            has = IsValidIndex(idx);
            if (!has)
            {
                //
                lookupIDX++;
                continue;
            }

            //
            // Retrieve Swing Bar ...
            has = resistence.Init(
                bar.symbol,
                bar.period,
                idx //
            );
            if (!has)
            {
                //
                idx = -1;
                lookupIDX++;
                resistence.Clean();
                continue;
            }

            //
            // Check Resistance High Shadow ...
            has = resistence.GetUp() > bar.high;
            if (!has)
            {
                //
                idx = -1;
                lookupIDX++;
                resistence.Clean();
                continue;
            }

            //
            if (IsValidIndex(idx))
            {
                break;
            }
        }

        //
        result = resistence.IsValid();

        //
        return result;
    }

    /**
     * Check Specified Bar Has Support or Resistance and Extract them ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  support: XBoxZone, reference to holds Detected Support ...
     * @param  resistance: XBoxZone, reference to holds Detected Resistance ...
     * @param  validationLength: int, Specified Validation Length ...
     * @param  loopback: int, Max Allowed Loopback Bars for Detection ...
     *
     * @return ( bool )
     */
    bool HasSupportResistance(
        XOHCL &bar,
        XBoxZone &support,
        XBoxZone &resistance,
        int validationLength = 7,
        int loopback = 100 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        support.Clean();
        resistance.Clean();

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 50);
        validationLength = NormalizeInt(validationLength, 3);

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        XOHCL iZBar;

        //
        // Support ...
        iZBar.Clean();
        bool hasSupport = HasSupport(
            bar,
            iZBar,
            validationLength,
            loopback //
        );
        if (hasSupport)
        {
            //
            hasSupport = ToSupportBox(
                iZBar,
                support,
                bar.time //
            );
        }

        //
        // Resistance ...
        iZBar.Clean();
        bool hasResistance = HasResistance(
            bar,
            iZBar,
            validationLength,
            loopback //
        );
        if (hasResistance)
        {
            //
            hasResistance = ToResistanceBox(
                iZBar,
                resistance,
                bar.time //
            );
        }

        //
        // Summarize Result ...
        result = hasSupport ||
                 hasResistance;

        //
        // Cleanup ...
        iZBar.Clean();

        //
        return result;
    }

    //
    // Analysers ...

    /**
     * Analyse Specified Bar ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  analysis: XPatternAnalysis, reference to holds Analysis result ...
     * @param  config: XPatternAnalysisConfig, reference to Provides Detector Configs ...
     *
     * @return ( bool )
     */
    bool AnalyseBarPatterns(
        XOHCL &bar,
        XPatternAnalysis &analysis,
        XPatternAnalysisConfig &config //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        analysis.Clean();

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        analysis.bar = bar;
        analysis.time = TimeCurrent();

        //
        XOHCL iSwing;
        XBoxZone iZone;
        ENUM_X_DIRECTION iDir;
        ENUM_X_BAR_PATTERN iPattern;

        //
        bool has;
        bool isBullish;
        bool isBearish;

        //
        bool canDetect;
        bool canDetectBullish;
        bool canDetectBearish;

        //
        // Bars Detectors ...

        //
        // XPV High Low Detection ...
        canDetect = config.CanDetect(X_BAR_PATTERN_HIGH) ||
                    config.CanDetect(X_BAR_PATTERN_LOW);
        if (canDetect)
        {
            //
            has = IsPV(
                bar,
                iDir,
                config.pvValidationLength,
                config.peakPriceType,
                config.valePriceType //
            );

            //
            canDetectBullish = config.CanDetect(X_BAR_PATTERN_LOW);
            canDetectBearish = config.CanDetect(X_BAR_PATTERN_HIGH);

            //
            isBullish =
                has &&
                IsXBullish(iDir) &&
                canDetectBullish;

            //
            isBearish =
                has &&
                IsXBearish(iDir) &&
                canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                iPattern =
                    isBearish
                        ? X_BAR_PATTERN_HIGH
                        : X_BAR_PATTERN_LOW;

                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Momentum ...
        iPattern = X_BAR_PATTERN_MOMENTUM;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsMomentumBar(
                bar,
                iDir,
                config.momentumBarValidationLength,
                config.momentumBarApprovedMultiplier //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Engulfed ...
        iPattern = X_BAR_PATTERN_ENGULFED;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsEngulfedBar(
                bar,
                iDir //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // TrueGap ...
        iPattern = X_BAR_PATTERN_TRUE_GAPED;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsTrueGapedBar(
                bar,
                iDir,
                config.trueGapApprovedStrength //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Pinned ...
        iPattern = X_BAR_PATTERN_PINNED;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsPinnedBar(
                bar,
                iDir,
                config.pinBarShadowApprovedMultiplier,
                config.pinBarRangeMultiplier,
                config.pinBarForceDirection //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Pattern Detectors ...

        //
        // Star ...
        iPattern = X_BAR_PATTERN_STAR;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsStarPattern(
                bar,
                iDir //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Piercing ...
        iPattern = X_BAR_PATTERN_PIERCING;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsPiercingPattern(
                bar,
                iDir //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Rising ...
        iPattern = X_BAR_PATTERN_RISING;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsRisingPattern(
                bar,
                iDir //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Flag ...
        iPattern = X_BAR_PATTERN_FLAG;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsFlagPattern(
                bar,
                iDir,
                iSwing,
                config.flagPatternPullbackLength //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );

                //
                analysis.flagSwing = iSwing;
            }
        }

        //
        // SignalKey ...
        iPattern = X_BAR_PATTERN_SIGNALKEY;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = IsSignalKeyBarPattern(
                bar,
                iDir,
                config.pinBarShadowApprovedMultiplier,
                config.pinBarRangeMultiplier,
                config.pinBarForceDirection //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        IsXBullish(iDir) &&
                        canDetectBullish;
            isBearish = has &&
                        IsXBearish(iDir) &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iDir //
                );
            }
        }

        //
        // Zone Detectors ...

        //
        // OB ...
        iPattern = X_BAR_PATTERN_OB;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = barAnalyser.IsOB(
                bar,
                iZone,
                config.fvgForceType,
                config.obForceTwoBar //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        iZone.IsBullish() &&
                        canDetectBullish;
            isBearish = has &&
                        iZone.IsBearish() &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iZone.dir //
                );

                //
                analysis.ob = iZone;
            }
        }

        //
        // FVG ...
        iPattern = X_BAR_PATTERN_FVG;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = barAnalyser.IsFVG(
                bar,
                iZone,
                config.fvgForceType //
            );

            //
            canDetectBullish = config.CanDetect(
                iPattern,
                X_DIRECTION_BULLISH //
            );
            canDetectBearish = config.CanDetect(
                iPattern,
                X_DIRECTION_BEARISH //
            );

            //
            isBullish = has &&
                        iZone.IsBullish() &&
                        canDetectBullish;
            isBearish = has &&
                        iZone.IsBearish() &&
                        canDetectBearish;

            //
            canDetect = isBullish ||
                        isBearish;
            if (canDetect)
            {
                //
                analysis.AddPattern(
                    iPattern,
                    iZone.dir //
                );

                //
                analysis.fvg = iZone;
            }
        }

        //
        // Support ...
        iPattern = X_BAR_PATTERN_SUPPORT;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = HasSupport(
                bar,
                iSwing,
                config.supportAndResistanceValidationLength,
                config.supportAndResistanceLoopbackLength //
            );
            if (has)
            {
                //
                has = ToSupportBox(
                    iSwing,
                    iZone,
                    bar.time,
                    config.supportAndResistanceValidationLength //
                );
                if (has)
                {
                    //
                    analysis.AddPattern(
                        iPattern,
                        iZone.dir //
                    );

                    //
                    analysis.support = iZone;
                }
            }
        }

        //
        // Resistance ...
        iPattern = X_BAR_PATTERN_RESISTANCE;
        canDetect = config.CanDetect(iPattern);
        if (canDetect)
        {
            //
            has = HasResistance(
                bar,
                iSwing,
                config.supportAndResistanceValidationLength,
                config.supportAndResistanceLoopbackLength //
            );
            if (has)
            {
                //
                has = ToResistanceBox(
                    iSwing,
                    iZone,
                    bar.time,
                    config.supportAndResistanceValidationLength //
                );
                if (has)
                {
                    //
                    analysis.AddPattern(
                        iPattern,
                        iZone.dir //
                    );

                    //
                    analysis.resistance = iZone;
                }
            }
        }

        //
        // Validate Analysis ...
        result = analysis.IsValid();

        //
        // Cleanup Resources ...
        iZone.Clean();
        iSwing.Clean();
        CleanDirection(iDir);

        //
        return result;
    }

    /**
     * Collect Required Order Blocks ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dest: XBoxZone, collection reference to holds detected zones ...
     * @param  zonesLoopback: int, zones detection loopback ...
     * @param  maxRequiredZone: int, number of required zones ...
     * @param  loopback: int, max allowed loopback lenght for zone Detection ...
     *
     * @return ( int )
     */
    int CollectOBs(
        XOHCL &bar,
        XBoxZone &dest[],
        int zonesLoopback = 5,
        int maxRequiredZone = 0,
        int loopback = 100 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 10);
        zonesLoopback = NormalizeInt(zonesLoopback, 3);
        maxRequiredZone = NormalizeInt(maxRequiredZone, 0);

        //
        // Validate ...
        bool has = bar.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        XBoxZone iBox;
        ENUM_X_DIRECTION iDir;
        int start = bar.Index();
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            iBox.Clean();
            iDir = X_DIRECTION_NONE;

            //
            has = bar.BarAt(i, iBar);
            has = has &&
                  HasOB(iBar, iDir, iBox, zonesLoopback);
            if (has)
            {
                //
                AddIfNotExists(
                    iBox,
                    dest //
                );
            }

            //
            has = maxRequiredZone <= 0 ||
                  (maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
            if (!has)
            {
                break;
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iBox.Clean();
        iDir = X_DIRECTION_NONE;

        //
        return result;
    }

    /**
     * Collect Required Fair Value Gaps ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dest: XBoxZone, collection reference to holds detected zones ...
     * @param  zonesLoopback: int, zones detection loopback ...
     * @param  maxRequiredZone: int, number of required zones ...
     * @param  loopback: int, max allowed loopback lenght for zone Detection ...
     *
     * @return ( int )
     */
    int CollectFVGs(
        XOHCL &bar,
        XBoxZone &dest[],
        int zonesLoopback = 5,
        int maxRequiredZone = 0,
        int loopback = 100 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 10);
        zonesLoopback = NormalizeInt(zonesLoopback, 3);
        maxRequiredZone = NormalizeInt(maxRequiredZone, 0);

        //
        // Validate ...
        bool has = bar.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        XBoxZone iBox;
        ENUM_X_DIRECTION iDir;
        int start = bar.Index();
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            iBox.Clean();
            iDir = X_DIRECTION_NONE;

            //
            has = bar.BarAt(i, iBar);
            has = has &&
                  HasFVG(iBar, iDir, iBox, zonesLoopback);
            if (has)
            {
                //
                AddIfNotExists(
                    iBox,
                    dest //
                );
            }

            //
            has = maxRequiredZone <= 0 ||
                  (maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
            if (!has)
            {
                break;
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iBox.Clean();
        iDir = X_DIRECTION_NONE;

        //
        return result;
    }

    /**
     * Collect Required Support and Resistance Zones ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  dest: XBoxZone, collection reference to holds detected zones ...
     * @param  zonesLoopback: int, zones detection loopback ...
     * @param  zonesValidationLength: int, zones validation length ...
     * @param  maxRequiredZone: int, number of required zones ...
     * @param  loopback: int, max allowed loopback lenght for zone Detection ...
     *
     * @return ( int )
     */
    int CollectZones(
        XOHCL &bar,
        XBoxZone &dest[],
        int zonesLoopback = 5,
        int zonesValidationLength = 21,
        int maxRequiredZone = 0,
        int loopback = 100 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);

        //
        // Normalize ...
        loopback = NormalizeInt(loopback, 10);
        zonesLoopback = NormalizeInt(zonesLoopback, 3);
        maxRequiredZone = NormalizeInt(maxRequiredZone, 0);
        zonesValidationLength = NormalizeInt(zonesValidationLength, 3);

        //
        // Validate ...
        bool has = bar.IsValid();
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        XBoxZone iBox;
        XOHCL iZoneBar;
        int start = bar.Index();
        int end = start + loopback;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            iBox.Clean();
            iZoneBar.Clean();

            //
            has = bar.BarAt(i, iBar);
            if (!has)
            {
                continue;
            }

            //
            // Support ...
            has = HasSupport(iBar, iZoneBar, zonesValidationLength, zonesLoopback);
            if (has)
            {
                //
                has = ToSupportBox(iZoneBar, iBox, bar.time, zonesValidationLength);
                if (has)
                {
                    //
                    AddIfNotExists(
                        iBox,
                        dest //
                    );
                }
            }

            //
            // Resistance ...
            has = HasResistance(iBar, iZoneBar, zonesValidationLength, zonesLoopback);
            if (has)
            {
                //
                has = ToResistanceBox(iZoneBar, iBox, bar.time, zonesValidationLength);
                if (has)
                {
                    //
                    AddIfNotExists(
                        iBox,
                        dest //
                    );
                }
            }

            //
            has = maxRequiredZone <= 0 ||
                  (maxRequiredZone > 0 && ArraySize(dest) < maxRequiredZone);
            if (!has)
            {
                break;
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iBox.Clean();
        iZoneBar.Clean();

        //
        return result;
    }

    /**
     * Detect and Collect Required Zones ...
     *
     * @param  bar: XOHCL, reference to Specified Bar ...
     * @param  zones: XBoxZone, collection reference to holds detected zones ...
     * @param  validateZones: bool, Specified to Detect only Valid Zones ...
     * @param  containsOBs: bool, Flag to use Order Block Zones ...
     * @param  containsFVGs: bool, Flag to use Fair Value Gap Zones ...
     * @param  containsSupports: bool, Flag to use Support Zones ...
     * @param  containsResistancess: bool, Flag to use Resistance Zones ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Zones Direction for Detecting ...
     * @param  sortby: ENUM_X_SORT_BY, Specified Zones Sorting Factor ...
     * @param  sortDir: ENUM_X_DIRECTION, Specified Zones Sorting Direction ...
     * @param  zonesRangeValidationFactor: double, Specified Zones Range Validation in Points ...
     * @param  zonesLoopback: int, loop back for Zones Detection ...
     * @param  zonesValidationLength: int, zones Validation Length ...
     * @param  count: int, number of required Zones ...
     * @param  loopback: int, max allowed looping back lenght for detection ...
     *
     * @return ( int )
     */
    int DetectZones(
        XOHCL &bar,
        XBoxZone &zones[],
        bool validateZones = true,
        bool containsOBs = true,
        bool containsFVGs = true,
        bool containsSupports = true,
        bool containsResistancess = true,
        int loopback = 500,
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE,
        ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
        ENUM_X_DIRECTION sortDir = X_DIRECTION_BEARISH,
        double zonesRangeValidationFactor = 10,
        int zonesLoopback = 5,
        int zonesValidationLength = 5,
        int count = 0 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(zones);

        //
        // Normalize ...
        count = NormalizeInt(count, 0);
        loopback = NormalizeInt(loopback, 50);
        zonesLoopback = NormalizeInt(zonesLoopback, 2);
        zonesValidationLength = NormalizeInt(zonesValidationLength, 3);

        //
        // Validate ...
        bool has = bar.IsValid() &&
                   IsXValid(sortDir) &&
                   IsXValid(sortBy) &&
                   (containsOBs || containsFVGs || containsSupports || containsResistancess);
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        XOHCL tmpBar;
        XPivot tmpPivot;
        XBoxZone tmpZone;
        ENUM_X_DIRECTION tmpDir;
        int idx = bar.Index();
        while (idx < bar.Index() + loopback)
        {
            //
            idx++;

            //
            iBar.Clean();
            tmpBar.Clean();
            tmpZone.Clean();
            tmpPivot.Clean();

            //
            has = bar.BarAt(idx, iBar);
            if (!has)
            {
                continue;
            }

            //
            // Collecting Zones ...

            //
            // Support ...
            if (containsSupports)
            {
                //
                if (!HasDirection(forDir) || IsXBullish(forDir))
                {
                    //
                    has = HasSupport(iBar, tmpBar, zonesValidationLength, zonesLoopback);
                    if (has)
                    {
                        //
                        has = ToBox(tmpBar, tmpZone, X_DIRECTION_BULLISH, X_BAR_PATTERN_SUPPORT);
                        if (has)
                        {
                            AddIfNotExists(tmpZone, zones);
                        }
                    }
                }
            }

            //
            // Resistance ...
            if (containsResistancess)
            {
                //
                if (!HasDirection(forDir) || IsXBearish(forDir))
                {
                    //
                    has = HasResistance(iBar, tmpBar, zonesValidationLength, zonesLoopback);
                    if (has)
                    {
                        //
                        has = ToBox(tmpBar, tmpZone, X_DIRECTION_BEARISH, X_BAR_PATTERN_RESISTANCE);
                        if (has)
                        {
                            AddIfNotExists(tmpZone, zones);
                        }
                    }
                }
            }

            //
            // Order Block ...
            if (containsOBs)
            {
                //
                has = HasOB(iBar, tmpDir, tmpZone, zonesLoopback);
                if (has && (!HasDirection(forDir) || tmpDir == forDir))
                {
                    AddIfNotExists(tmpZone, zones);
                }
            }

            //
            // Fair Value Gaps ...
            if (containsFVGs)
            {
                //
                has = HasFVG(iBar, tmpDir, tmpZone, zonesLoopback);
                if (has && (!HasDirection(forDir) || tmpDir == forDir))
                {
                    AddIfNotExists(tmpZone, zones);
                }
            }
        }

        //
        // Validate Zones Exists ...
        result = ArraySize(zones);
        has = IsValidSize(result);
        if (!has)
        {
            return result;
        }

        //
        // Update Zones Time ...
        UpdateToTime(zones, bar.time);

        //
        // Validate Zones ...
        if (validateZones)
        {
            //
            // Apply Zones Validation Here ...
            RemoveBreakedZones(zones, true);
        }

        //
        // Validate Zones Range ...
        if (zonesRangeValidationFactor > 0)
        {
            //
            double points = GetPoints(bar.symbol);
            double minAllowedRange = points * zonesRangeValidationFactor;
            ValidateZonesRange(zones, minAllowedRange);
        }

        //
        // Apply Sorting ...
        Sort(zones, sortBy, sortDir);

        //
        // Validate Zones Count ...
        if (count > 0)
        {
            CleanupArray(zones, count);
        }

        //
        result = ArraySize(zones);

        //
        // Cleanup ...
        iBar.Clean();
        tmpBar.Clean();
        tmpZone.Clean();
        tmpPivot.Clean();

        //
        return result;
    }

    /**
     * Filter Zones ...
     *
     * @param  bar: XOHCL, reference to Specified Filtering Bar ...
     * @param  source: XBoxZone, reference collection to hold's filtered zones ...
     * @param  filterDir: ENUM_X_DIRECTION, specified Filter Direction ...
     * @param  priceType: ENUM_X_PRICE, Specified Bar's Price type to Use for Filtering Zones ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Zone's Direction to Handle Filtering ...
     *
     * @return ( int )
     */
    int FilterZones(
        XOHCL &bar,
        XBoxZone &source[],
        ENUM_X_DIRECTION filterDir,
        ENUM_X_PRICE priceType = X_PRICE_CLOSE,
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XBoxZone tmp[];
        Copy(
            source,
            tmp //
        );
        XClean(source);

        //
        // Validate ...
        bool has = bar.IsValid() &&
                   HasChild(tmp) &&
                   IsXValid(priceType) &&
                   HasDirection(filterDir);
        if (!has)
        {
            //
            XClean(tmp);
            return result;
        }

        //
        XBoxZone iBox;
        double price = bar.GetPrice(priceType);
        bool isBullish = IsXBullish(filterDir);
        while (HasChild(tmp))
        {
            //
            iBox = tmp[0];
            ArrayRemove(tmp, 0, 1);

            //
            has =
                //
                // Direction Pass ...
                (!HasDirection(forDir)
                     ? true
                     : forDir == iBox.dir)
                //
                &&
                //
                // Price Pass ...
                (isBullish
                     ? price > iBox.lower
                     : price < iBox.upper);
            if (has)
            {
                AddIfNotExists(iBox, source);
            }

            //
            iBox.Clean();
        }

        //
        result = ArraySize(source);

        //
        // Cleanup ...
        XClean(tmp);
        iBox.Clean();

        //
        return result;
    }

    //
    // Protected ...
  protected:
    //

    //
    // Tools / Actions ...

    /**
     * Check Zones and Manage them ...
     *
     * @param  forBar: XOHCL, reference to Specified Bar ...
     * @param  _zones: XBoxZone, reference to Specified Zone Collection ...
     * @param  sortBy: X_SORT_BY_DATE, Sort Zones by ...
     * @param  sortDir: ENUM_X_DIRECTION, Sort Zones in Direction ...
     * @param  maxAllowed: int, Specified Max Allowed Zones ...
     */
    void CheckZones(
        XOHCL &forBar,
        XBoxZone &_zones[],
        ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
        ENUM_X_DIRECTION sortDir = X_DIRECTION_BEARISH,
        int maxAllowed = 50 //
    )
    {
        //
        // Normalize ...
        maxAllowed = NormalizeInt(maxAllowed, 0);

        //
        // Validate ...
        bool has = forBar.IsValid() &&
                   HasChild(_zones);
        if (!has)
        {
            return;
        }

        //
        // Looping Through Zones ...
        int count = ArraySize(_zones);
        for (int i = 0; i < count; i++)
        {
            //
            // Check Break ...
            has = IsXValid(_zones[i].breakAt);
            if (has)
            {
                continue;
            }

            //
            _zones[i].to = forBar.time;

            //
            // Check for Test ...
            has = IsTested(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                //
                Add(
                    forBar.time,
                    _zones[i].tests //
                );
            }

            //
            // Check for Inside ...
            has = IsInside(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                //
                Add(
                    forBar.time,
                    _zones[i].insides //
                );
            }

            //
            // Check for Break ...
            has = IsBreaked(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                //
                Add(
                    forBar.time,
                    _zones[i].breaks //
                );
            }

            //
            // Check for Act ...
            has = IsActed(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                //
                Add(
                    forBar.time,
                    _zones[i].acteds //
                );
            }

            //
            // Check for FakeBreakes ...
            has = IsFakeBreaked(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                //
                Add(
                    forBar.time,
                    _zones[i].fakeBreaks //
                );
            }

            //
            // Check for Valid Breakes ...
            has = IsValidBreaked(
                forBar,
                _zones[i] //
            );
            if (has)
            {
                _zones[i].breakAt = forBar.time;
            }
        }

        //
        // Apply Sort and Cleanup ...
        Sort(
            _zones,
            sortBy,
            sortDir,
            maxAllowed //
        );
    }

    //
    // Private ...
  private:
    //
    // Props ...
    XCBarAnalyser *barAnalyser;

    //
    // Helpers ...

    /**
     * Clean Specified Referenced Direction ...
     *
     * @param  dir: ENUM_X_DIRECTION
     */
    void CleanDirection(ENUM_X_DIRECTION &dir)
    {
        dir = X_DIRECTION_NONE;
    }
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-md5.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCMD5
// Description: Provides MD5 Hashing Requirements ...
//
//
// 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

//
// START Definitions ...
//

//
static uchar _md5_PADDING[64] =
    {
        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

//
#define _md5_F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define _md5_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define _md5_H(x, y, z) ((x) ^ (y) ^ (z))
#define _md5_I(x, y, z) ((y) ^ ((x) | (~z)))
#define _md5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))

//
#define _md5_FF(a, b, c, d, x, s, ac)                    \
    {                                                    \
        (a) += _md5_F((b), (c), (d)) + (x) + (uint)(ac); \
        (a) = _md5_ROTATE_LEFT((a), (s));                \
        (a) += (b);                                      \
    }

//
#define _md5_GG(a, b, c, d, x, s, ac)                    \
    {                                                    \
        (a) += _md5_G((b), (c), (d)) + (x) + (uint)(ac); \
        (a) = _md5_ROTATE_LEFT((a), (s));                \
        (a) += (b);                                      \
    }

//
#define _md5_HH(a, b, c, d, x, s, ac)                    \
    {                                                    \
        (a) += _md5_H((b), (c), (d)) + (x) + (uint)(ac); \
        (a) = _md5_ROTATE_LEFT((a), (s));                \
        (a) += (b);                                      \
    }

//
#define _md5_II(a, b, c, d, x, s, ac)                    \
    {                                                    \
        (a) += _md5_I((b), (c), (d)) + (x) + (uint)(ac); \
        (a) = _md5_ROTATE_LEFT((a), (s));                \
        (a) += (b);                                      \
    }

//
#define _md5_INIT_STATE_0 0x67452301
#define _md5_INIT_STATE_1 0xefcdab89
#define _md5_INIT_STATE_2 0x98badcfe
#define _md5_INIT_STATE_3 0x10325476

//
#define _md5_S11 7
#define _md5_S12 12
#define _md5_S13 17
#define _md5_S14 22

//
#define _md5_S21 5
#define _md5_S22 9
#define _md5_S23 14
#define _md5_S24 20

//
#define _md5_S31 4
#define _md5_S32 11
#define _md5_S33 16
#define _md5_S34 23

//
#define _md5_S41 6
#define _md5_S42 10
#define _md5_S43 15
#define _md5_S44 21

//
// END Definitions ...
//

//
// XCMD5 a library for Hashing ...
class XCMD5
{
    //
    // Public Provides ...
  public:
    //
    // Protected Provides ...

    //
    // Constructor ...
    XCMD5(void)
    {
    }

    //
    // Deconstructor ...
    ~XCMD5(void)
    {
    }

    //
    // Hash Specified Char Array ...
    string Hash(
        uchar &mSource[], // Specify Char Array to Hash
        int mLength = 0   // Specify Length of Char Array which required to hash
    )
    {
        //
        string result = "";

        //
        int sourceCount = ArraySize(mSource);

        //
        // Validate Args ...
        if (
            sourceCount <= 0 ||
            (sourceCount > 0 && mLength > sourceCount))
        {
            return result;
        }

        //
        // Normalize Args ...
        if (mLength == 0)
        {
            mLength = sourceCount;
        }

        //
        // Init MD5 ...
        MD5Init();

        //
        // Update Buffer ...
        MD5Update(mSource, mLength);

        //
        // Calculate Result ...
        result = MD5Final();

        //
        return result;
    }

    //
    // Hash Specified String ...
    string Hash(
        string mSource // Specified String
    )
    {
        //
        string result = "";

        //
        // Converts String to Char Array ...
        uchar bytes[];
        StringToCharArray(
            mSource,
            bytes,
            0,
            StringLen(mSource));

        //
        result = Hash(
            bytes,
            ArraySize(bytes));

        //
        return result;
    }

  protected:
    //
    // Private Provides ...
  private:
    //
    uint m_lMD5[4];
    uint m_nCount[2];
    uchar m_lpszBuffer[64];

    //
    // Convert Byte to DWord ...
    void ByteToDWord(int &out[], uint &in[], uint len)
    {
        //
        uint i = 0;
        uint j = 0;

        //
        for (; j < len; i++, j += 4)
        {
            out[i] = (int)in[j] | (int)in[j + 1] << 8 | (int)in[j + 2] << 16 | (int)in[j + 3] << 24;
        }
    }

    //
    // Convert DWord to Byte ...
    void DWordToByte(uchar &out[], uint &in[], uint len)
    {
        //
        uint i = 0;
        uint j = 0;

        //
        for (; j < len; i++, j += 4)
        {
            //
            out[j] = (uchar)(in[i] & 0xff);
            out[j + 1] = (uchar)((in[i] >> 8) & 0xff);
            out[j + 2] = (uchar)((in[i] >> 16) & 0xff);
            out[j + 3] = (uchar)((in[i] >> 24) & 0xff);
        }
    }

    //
    // Init MD5 Array ...
    void MD5Init()
    {
        //
        ArrayInitialize(m_lpszBuffer, 64);

        //
        m_nCount[0] = m_nCount[1] = 0;
        m_lMD5[0] = _md5_INIT_STATE_0;
        m_lMD5[1] = _md5_INIT_STATE_1;
        m_lMD5[2] = _md5_INIT_STATE_2;
        m_lMD5[3] = _md5_INIT_STATE_3;
    }

    //
    // Update MD5 ...
    void MD5Update(uchar &inBuf[], uint inLen)
    {
        //
        int i, ii;
        int mdi;

        //
        uint in[16];
        int i0 = 0;

        //
        mdi = (int)((m_nCount[0] >> 3) & 0x3F);

        //
        if ((m_nCount[0] + ((uint)inLen << 3)) < m_nCount[0])
        {
            m_nCount[1]++;
        }

        //
        m_nCount[0] += ((uint)inLen << 3);
        m_nCount[1] += ((uint)inLen >> 29);

        //
        while ((inLen--) > 0)
        {
            //
            m_lpszBuffer[mdi++] = inBuf[i0++];
            if (mdi == 0x40)
            {
                //
                for (i = 0, ii = 0; i < 16; i++, ii += 4)
                {
                    in[i] = (((uint)m_lpszBuffer[ii + 3]) << 24) | (((uint)m_lpszBuffer[ii + 2]) << 16) | (((uint)m_lpszBuffer[ii + 1]) << 8) | ((uint)m_lpszBuffer[ii]);
                }

                //
                Transform(m_lMD5, in);

                //
                mdi = 0;
            }
        }
    }

    //
    // Finalize an MD5 Expression ...
    string MD5Final()
    {
        //
        uchar bits[8];
        int nIndex;
        uint nPadLen;
        const int nMD5Size = 16;
        uchar lpszMD5[16];
        string temp;
        string out = "";
        int i;

        //
        DWordToByte(bits, m_nCount, 8);
        nIndex = (int)((m_nCount[0] >> 3) & 0x3f);
        nPadLen = (nIndex < 56) ? (56 - nIndex) : (120 - nIndex);
        MD5Update(_md5_PADDING, nPadLen);
        MD5Update(bits, 8);
        DWordToByte(lpszMD5, m_lMD5, nMD5Size);

        //
        for (i = 0; i < nMD5Size; i++)
        {
            //
            if (lpszMD5[i] == 0)
            {
                temp = "00";
            }
            else if (lpszMD5[i] <= 15)
            {
                temp = StringFormat("0%x", lpszMD5[i]);
            }
            else
            {
                temp = StringFormat("%x", lpszMD5[i]);
            }

            //
            out += temp;
        }

        //
        lpszMD5[0] = '\0';

        //
        return (out);
    }

    //
    // Transform Buffers ...
    void Transform(uint &buf[], uint &in[])
    {
        //
        uint a = buf[0], b = buf[1], c = buf[2], d = buf[3];

        //
        _md5_FF(a, b, c, d, in[0], _md5_S11, 0xD76AA478);
        _md5_FF(d, a, b, c, in[1], _md5_S12, 0xE8C7B756);
        _md5_FF(c, d, a, b, in[2], _md5_S13, 0x242070DB);
        _md5_FF(b, c, d, a, in[3], _md5_S14, 0xC1BDCEEE);
        _md5_FF(a, b, c, d, in[4], _md5_S11, 0xF57C0FAF);
        _md5_FF(d, a, b, c, in[5], _md5_S12, 0x4787C62A);
        _md5_FF(c, d, a, b, in[6], _md5_S13, 0xA8304613);
        _md5_FF(b, c, d, a, in[7], _md5_S14, 0xFD469501);
        _md5_FF(a, b, c, d, in[8], _md5_S11, 0x698098D8);
        _md5_FF(d, a, b, c, in[9], _md5_S12, 0x8B44F7AF);
        _md5_FF(c, d, a, b, in[10], _md5_S13, 0xFFFF5BB1);
        _md5_FF(b, c, d, a, in[11], _md5_S14, 0x895CD7BE);
        _md5_FF(a, b, c, d, in[12], _md5_S11, 0x6B901122);
        _md5_FF(d, a, b, c, in[13], _md5_S12, 0xFD987193);
        _md5_FF(c, d, a, b, in[14], _md5_S13, 0xA679438E);
        _md5_FF(b, c, d, a, in[15], _md5_S14, 0x49B40821);

        //
        _md5_GG(a, b, c, d, in[1], _md5_S21, 0xF61E2562);
        _md5_GG(d, a, b, c, in[6], _md5_S22, 0xC040B340);
        _md5_GG(c, d, a, b, in[11], _md5_S23, 0x265E5A51);
        _md5_GG(b, c, d, a, in[0], _md5_S24, 0xE9B6C7AA);
        _md5_GG(a, b, c, d, in[5], _md5_S21, 0xD62F105D);
        _md5_GG(d, a, b, c, in[10], _md5_S22, 0x02441453);
        _md5_GG(c, d, a, b, in[15], _md5_S23, 0xD8A1E681);
        _md5_GG(b, c, d, a, in[4], _md5_S24, 0xE7D3FBC8);
        _md5_GG(a, b, c, d, in[9], _md5_S21, 0x21E1CDE6);
        _md5_GG(d, a, b, c, in[14], _md5_S22, 0xC33707D6);
        _md5_GG(c, d, a, b, in[3], _md5_S23, 0xF4D50D87);
        _md5_GG(b, c, d, a, in[8], _md5_S24, 0x455A14ED);
        _md5_GG(a, b, c, d, in[13], _md5_S21, 0xA9E3E905);
        _md5_GG(d, a, b, c, in[2], _md5_S22, 0xFCEFA3F8);
        _md5_GG(c, d, a, b, in[7], _md5_S23, 0x676F02D9);
        _md5_GG(b, c, d, a, in[12], _md5_S24, 0x8D2A4C8A);

        //
        _md5_HH(a, b, c, d, in[5], _md5_S31, 0xFFFA3942);
        _md5_HH(d, a, b, c, in[8], _md5_S32, 0x8771F681);
        _md5_HH(c, d, a, b, in[11], _md5_S33, 0x6D9D6122);
        _md5_HH(b, c, d, a, in[14], _md5_S34, 0xFDE5380C);
        _md5_HH(a, b, c, d, in[1], _md5_S31, 0xA4BEEA44);
        _md5_HH(d, a, b, c, in[4], _md5_S32, 0x4BDECFA9);
        _md5_HH(c, d, a, b, in[7], _md5_S33, 0xF6BB4B60);
        _md5_HH(b, c, d, a, in[10], _md5_S34, 0xBEBFBC70);
        _md5_HH(a, b, c, d, in[13], _md5_S31, 0x289B7EC6);
        _md5_HH(d, a, b, c, in[0], _md5_S32, 0xEAA127FA);
        _md5_HH(c, d, a, b, in[3], _md5_S33, 0xD4EF3085);
        _md5_HH(b, c, d, a, in[6], _md5_S34, 0x04881D05);
        _md5_HH(a, b, c, d, in[9], _md5_S31, 0xD9D4D039);
        _md5_HH(d, a, b, c, in[12], _md5_S32, 0xE6DB99E5);
        _md5_HH(c, d, a, b, in[15], _md5_S33, 0x1FA27CF8);
        _md5_HH(b, c, d, a, in[2], _md5_S34, 0xC4AC5665);

        //
        _md5_II(a, b, c, d, in[0], _md5_S41, 0xF4292244);
        _md5_II(d, a, b, c, in[7], _md5_S42, 0x432AFF97);
        _md5_II(c, d, a, b, in[14], _md5_S43, 0xAB9423A7);
        _md5_II(b, c, d, a, in[5], _md5_S44, 0xFC93A039);
        _md5_II(a, b, c, d, in[12], _md5_S41, 0x655B59C3);
        _md5_II(d, a, b, c, in[3], _md5_S42, 0x8F0CCC92);
        _md5_II(c, d, a, b, in[10], _md5_S43, 0xFFEFF47D);
        _md5_II(b, c, d, a, in[1], _md5_S44, 0x85845DD1);
        _md5_II(a, b, c, d, in[8], _md5_S41, 0x6FA87E4F);
        _md5_II(d, a, b, c, in[15], _md5_S42, 0xFE2CE6E0);
        _md5_II(c, d, a, b, in[6], _md5_S43, 0xA3014314);
        _md5_II(b, c, d, a, in[13], _md5_S44, 0x4E0811A1);
        _md5_II(a, b, c, d, in[4], _md5_S41, 0xF7537E82);
        _md5_II(d, a, b, c, in[11], _md5_S42, 0xBD3AF235);
        _md5_II(c, d, a, b, in[2], _md5_S43, 0x2AD7D2BB);
        _md5_II(b, c, d, a, in[9], _md5_S44, 0xEB86D391);

        //
        buf[0] += a;
        buf[1] += b;
        buf[2] += c;
        buf[3] += d;
    }
};


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-poi.drawer.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XPOIDrawer
// Description: Provides Point of Interests
// Drawing Tools ...
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-cobject.class.mq5"
#include "../Libraries/x-saherelm.x-pivot.analysis.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"

//
// Definitions ...

//
// Implementations ...
class XCPOIDrawer : public XCBase
{
    //
  public:
    //
    // Props ...

    //
    XPOIStyle defaultBullishStyle;
    XPOIStyle defaultBearishStyle;

    //
    XPOIStyle boxBullishStyle;
    XPOIStyle boxBearishStyle;

    //
    CArrayObj drawnObjects;

    //
    // Constructor ...
    XCPOIDrawer()
    {
        //
        Default();
    }

    //
    // Deconstructor ...
    ~XCPOIDrawer()
    {
        //
        boxBullishStyle.Clean();
        boxBearishStyle.Clean();
        defaultBullishStyle.Clean();
        defaultBearishStyle.Clean();
    }

    //
    // Setter Getter (s) ...

    /**
     * Get Prefix ...
     *
     * @return ( string )
     */
    string Prefix()
    {
        return mPrefix;
    }

    /**
     * Set Prifx ...
     *
     * @param  value: String ...
     */
    void Prefix(string value)
    {
        mPrefix = value;
    }

    /**
     * Get Chart ID ...
     *
     * @return ( long )
     */
    long ChartIdentification()
    {
        return mChartIdentification;
    }

    /**
     * Set Chart ID ...
     *
     * @param  value: Long ...
     */
    void ChartIdentification(long value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mChartIdentification = value;
    }

    /**
     * Get SubWindow Id ...
     *
     * @return ( int )
     */
    int SubWindowIdentification()
    {
        return mSubWindowIdentification;
    }

    /**
     * Set SubWindow Id ...
     *
     * @param  value: Integer ...
     */
    void SubWindowIdentification(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mSubWindowIdentification = value;
    }

    //
    // Virtuals ...

    /**
     * Apply Default Configurations ...
     */
    virtual void Default()
    {
        //
        // Apply Default Props ...

        //
        Prefix(NULL);
        ChartIdentification(0);
        SubWindowIdentification(0);

        //
        defaultBullishStyle.width = 1;
        defaultBullishStyle.clr = clrLime;
        defaultBullishStyle.style = STYLE_SOLID;

        //
        defaultBearishStyle.width = 1;
        defaultBearishStyle.clr = clrRed;
        defaultBearishStyle.style = STYLE_SOLID;

        //
        boxBullishStyle = defaultBullishStyle;
        boxBearishStyle = defaultBearishStyle;
    }

    //
    // Drawers ...

    /**
     * Draw Specified Bar ...
     *
     * @param  bar: XOHCL, refrence to Specified Bar ...
     * @param  object: XCOHCLObject, pointer reference for Drawn Object ...
     * @param  to: datetime, Specified To Time ...
     *
     * @return ( bool )
     */
    bool DrawBar(
        XOHCL &bar,
        XCOHCLObject *&object,
        datetime to = NULL //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = NULL;

        //
        // Validate ...
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        string prefix = Prefix();
        ulong chartId = ChartIdentification();
        int window = SubWindowIdentification();
        to = IsSpecifiedValid(to)
                 ? to
                 : ((datetime)((int)bar.time) + (PeriodSeconds(bar.period) - 1));
        object = new XCOHCLObject();
        result = object.Create(
            chartId,
            window,
            bar,
            to,
            prefix //
        );
        if (result)
        {
            //
            ApplyBarStyle(
                object,
                bar //
            );
        }

        //
        return result;
    }

    /**
     * Draw Bar Arrow Object ...
     *
     * @param  bar: XOHCL instance Reference ...
     * @param  object: XCBarArrowObject, pointer reference for Drawn Object ...
     * @param  priceType: ENUM_X_PRICE member ...
     * @param  arrow: int, Arrow Code ...
     * @param  clr: color, Arro Color ...
     * @param  width: int, Arrow Width ...
     * @param  anchor: ENUM_ARROW_ANCHOR, anchor of Arrow ...
     * @param  name: string, name of Arrow ...
     *
     * @return ( bool )
     */
    bool DrawBarArrow(
        XOHCL &bar,
        XCBarArrowObject *&object,
        ENUM_X_PRICE priceType,
        int arrow = 159,
        color clr = clrYellow,
        int width = 3,
        ENUM_ARROW_ANCHOR anchor = ANCHOR_BOTTOM,
        string name = NULL,
        bool ignoreBarTag = true,
        int threshold = 5 //
    )
    {
        //
        bool result = false;

        //
        object = NULL;

        //
        result = bar.IsValid();
        if (!result)
        {
            return result;
        }

        //
        string barTag = bar.GetTag(name);

        //
        string prefix = Prefix();
        long chartID = ChartIdentification();
        int window = SubWindowIdentification();

        //
        prefix =
            (IsValid(prefix)
                 ? prefix + "_"
                 : prefix) +
            (IsSpecifiedValid(name)
                 ? name
                 : "") +
            (ignoreBarTag ? "" : ToMD5(barTag));

        //
        object = new XCBarArrowObject();
        result = object
                     .CreateByBar(
                         chartID,
                         prefix,
                         window,
                         arrow,
                         bar,
                         priceType,
                         threshold //
                     );
        if (result)
        {
            //
            object.ArrowColor(clr);
            object.ArrowWidth(width);
            object.ArrowAnchor(anchor);
        }

        //
        return result;
    }

    /**
     * Dar Specified Pivot ...
     *
     * @param  pivot: XPivot, Specified Pivot ...
     * @param  object: XCBarArrowObject, pointer reference for Drawn Object ...
     * @param  peakArrow: int, Specified Arrow Code for Peak Pivots ...
     * @param  valeArrow: int, Specified Arrow Code for Vale Pivots ...
     * @param  threshold: int, distance between price and arrow in Point ...
     *
     * @return ( bool )
     */
    bool DrawPivot(
        XPivot &pivot,
        XCBarArrowObject *&object,
        int peakArrow = 159,
        int valeArrow = 159,
        int threshold = 5 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = NULL;

        //
        // Normalize ...
        threshold = NormalizeInt(threshold, 5);
        peakArrow = NormalizeInt(peakArrow, 159);
        valeArrow = NormalizeInt(valeArrow, 159);

        //
        // Validate ...
        result = pivot.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Get Pivot's Bar ...
        XOHCL bar;
        result = pivot.GetBar(bar);
        if (!result)
        {
            return result;
        }

        //
        int arrow = pivot.IsPeak()
                        ? peakArrow
                        : valeArrow;
        string name = pivot.GetObjectID();
        ENUM_X_PRICE type = pivot.IsPeak()
                                ? X_PRICE_HIGH
                                : X_PRICE_LOW;
        XPOIStyle style = pivot.IsPeak()
                              ? defaultBearishStyle
                              : defaultBullishStyle;
        ENUM_ARROW_ANCHOR anchor = pivot.IsPeak()
                                       ? ANCHOR_BOTTOM
                                       : ANCHOR_TOP;
        object = new XCBarArrowObject();
        result = object.CreateByBar(
            ChartIdentification(),
            name,
            SubWindowIdentification(),
            arrow,
            bar,
            type,
            threshold //
        );
        if (result)
        {
            //
            // Apply Styles ...
            object.ArrowAnchor(anchor);
            ApplyBarArrowStyle(
                object,
                style //
            );
        }

        //
        // Cleanup Resources ...
        bar.Clean();

        //
        return result;
    }

    /**
     * Draw Specified Pivot Collection ...
     *
     * @param  pivots: XPivot, reference Collection to Draw ...
     * @param  draws: CArrayObj, pointer reference to Hold Drawn Objects ...
     * @param  peakArrow: int, Specified Arrow Code for Peak Pivots ...
     * @param  valeArrow: int, Specified Arrow Code for Vale Pivots ...
     * @param  threshold: int, distance between price and arrow in Point ...
     *
     * @return ( int )
     */
    int DrawPivots(
        XPivot &pivots[],
        CArrayObj *&draws,
        int peakArrow = 159,
        int valeArrow = 159,
        int threshold = 5 //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        if (draws == NULL)
        {
            draws = new CArrayObj();
        }
        draws.Clear();

        //
        // Validate ...
        if (!HasChild(pivots))
        {
            return result;
        }

        //
        bool has = false;
        XCBarArrowObject *iObj;
        int count = ArraySize(pivots);
        for (int i = 0; i < count; i++)
        {
            //
            // Draw Indexed Pivot ...
            has = DrawPivot(
                pivots[i],
                iObj,
                peakArrow,
                valeArrow,
                threshold //
            );

            //
            if (has)
            {
                draws.Add(iObj);
            }

            //
            ZeroMemory(iObj);
        }

        //
        result = draws.Total();

        //
        return result;
    }

    /**
     * Draw Specified Box ...
     *
     * @param  object: XCBoxObject, pointer reference for Drawn Object ...
     * @param  name: Box Name ...
     * @param  upper: double, Box Upper ...
     * @param  lower: double, Box Lower ...
     * @param  from: datetime, From Time ...
     * @param  to: datetime, To Time ...
     * @param  at: datetime, Specified At Time ...
     *
     * @return ( bool )
     */
    bool DrawBox(
        XCBoxObject *&object,
        string name,
        double upper,
        double lower,
        datetime from,
        datetime to,
        datetime at //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = NULL;

        //
        // Normalize ...
        to = NormalizeTime(to);
        from = NormalizeTime(from);

        //
        // Validate ...
        result =
            upper > 0 &&
            lower > 0 &&
            to > from &&
            IsSpecifiedValid(to) &&
            IsSpecifiedValid(name) &&
            IsSpecifiedValid(from) &&
            upper > lower;
        if (!result)
        {
            return result;
        }

        //
        string prefix = Prefix();
        long chartID = ChartIdentification();
        int window = SubWindowIdentification();

        //
        object = new XCBoxObject();
        result = object.Create(
            chartID,
            window,
            name,
            upper,
            lower,
            from,
            to,
            at,
            prefix //
        );

        //
        return result;
    }

    /**
     * Draw Specific Box  ...
     *
     * @param  box: XBoxZone, Specified Box ...
     * @param  object: XCBarArrowObject, pointer reference for Drawn Object ...
     * @param  ignoreAt: bool, Ignore Box at or not ...
     *
     * @return ( bool )
     */
    bool DrawBox(
        XBoxZone &box,
        XCBoxObject *&object,
        bool ignoreAt = true,
        string name = NULL //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = NULL;

        //
        // Validate ...
        result = box.IsValid();
        if (!result)
        {
            return result;
        }

        //
        datetime at = box.at;
        if (ignoreAt)
        {
            at = NULL;
        }

        //
        name = IsSpecifiedValid(name)
                   ? name
                   : box.GetTag();
        result = DrawBox(
            object,
            name,
            box.upper,
            box.lower,
            box.from,
            box.to,
            at //
        );
        if (result)
        {
            //
            // Apply Style ...
            XPOIStyle style;
            GetBoxStyle(
                style,
                box.dir //
            );

            //
            ApplyBoxStyle(
                object,
                style //
            );

            //
            style.Clean();
        }

        //
        return result;
    }

    /**
     * Draw a Collection of Boxes ...
     *
     * @param  boxes: reference collection to Provide Boxes ...
     * @param  objects: reference pointer to holds drawn Objects ...
     * @param  ignoreAt: bool, ignore Box At Drawn ...
     *
     * @return ( int )
     */
    int DrawBoxes(
        XBoxZone &boxes[],
        CArrayObj *&objects,
        bool useTypeName = true,
        bool ignoreAt = true //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        objects = new CArrayObj();

        //
        // Validate ...
        if (!HasChild(boxes))
        {
            return result;
        }

        //
        bool has = false;
        XCBoxObject *iObj;
        string name = NULL;
        int count = ArraySize(boxes);
        for (int i = 0; i < count; i++)
        {
            //
            if (useTypeName)
            {
                name = boxes[i].type;
            }

            //
            has = DrawBox(
                boxes[i],
                iObj,
                ignoreAt,
                name //
            );
            if (has)
            {
                objects.Add(iObj);
            }

            //
            ZeroMemory(iObj);
        }

        //
        result = objects.Total();

        //
        return result;
    }

    /**
     * Draw Specified Signal ...
     *
     * @param  signal: XSignal, Specified Signal ...
     * @param  object: XCSignalObject, pointer reference for Drawn Object ...
     * @param  length: int, Signal Length ...
     */
    bool DrawSignal(
        XSignal &signal,
        XCSignalObject *&object,
        int length = 3 //
    )
    {
        //
        bool result = false;

        //
        object = NULL;
        length = NormalizeInt(length, 3);

        //
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }

        //
        string prefix = Prefix();
        long chartID = ChartIdentification();
        int window = SubWindowIdentification();

        //
        object = new XCSignalObject();
        result = object.Create(
            chartID,
            window,
            signal,
            length,
            prefix //
        );
        if (!result)
        {
            return result;
        }

        //
        object.TPWidth(1);
        object.TPColor(clrGreen);
        object.TPWidth(STYLE_DOT);

        //
        object.SLWidth(1);
        object.SLColor(clrRed);
        object.SLWidth(STYLE_DOT);

        //
        object.TargetWidth(1);
        object.TargetColor(clrLightBlue);
        object.TargetWidth(STYLE_DOT);

        //
        object.EntryWidth(1);
        object.EntryColor(clrYellow);
        object.EntryWidth(STYLE_DOT);

        //
        return result;
    }

    /**
     * Draw Specified Trend Line ...
     *
     * @param  fromPrice: double, from Price ...
     * @param  fromTime: datetime, from Time ...
     * @param  toPrice: double, to Price ...
     * @param  toTime: datetime, to Time ...
     * @param  object: CChartObjectTrend, pointer reference for Drawn Object ...
     * @param  name: Object Name ...
     * @param  rayRight: bool, ray to Right ...
     * @param  rayLeft: boo, ray to Left ...
     *
     * @return ( bool )
     */
    bool DrawTrendLine(
        double fromPrice,
        datetime fromTime,
        double toPrice,
        datetime toTime,
        CChartObjectTrend *&object,
        string name,
        bool rayRight = false,
        bool rayLeft = false //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = new CChartObjectTrend();

        //
        // Validate ...
        result =
            toPrice > 0 &&
            fromPrice > 0 &&
            IsSpecifiedValid(toTime) &&
            IsSpecifiedValid(fromTime) &&
            toTime > fromTime;
        if (!result)
        {
            return result;
        }

        //
        // Create Object ...
        result = object.Create(
            ChartIdentification(),
            name,
            SubWindowIdentification(),
            fromTime,
            fromPrice,
            toTime,
            toPrice //
        );
        if (result)
        {
            //
            ENUM_X_DIRECTION dir =
                fromPrice < toPrice
                    ? X_DIRECTION_BULLISH
                : fromPrice > toPrice
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
            ApplyTrendLineStyle(
                object,
                dir //
            );

            //
            object.RayLeft(rayLeft);
            object.RayRight(rayRight);
        }

        //
        return result;
    }

    /**
     * Draw Trend Line based on Swings Points ...
     *
     * @param  swings: XOHCL, Specified Swings ...
     * @param  dir: ENUM_X_DIRECTION, Specified Trend Direction ...
     * @param  object: CChartObjectTrend, pointer reference for Drawn Object ...
     * @param  rayRight: Ray to Right ...
     * @param  rayLeft: Ray to Left ...
     *
     * @return ( bool )
     */
    bool DrawSwingTrend(
        XOHCL &swings[],
        ENUM_X_DIRECTION dir,
        CChartObjectTrend *&object,
        bool rayRight = false,
        bool rayLeft = false //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        object = NULL;

        //
        int count = ArraySize(swings);
        result = HasChild(swings) &&
                 count >= 2 &&
                 HasDirection(dir);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = IsSpecifiedBullish(dir);

        //
        XOHCL _swings[];
        Copy(
            swings,
            _swings //
        );
        SortBar(
            _swings,
            X_SORT_BY_DATE,
            X_DIRECTION_BULLISH //
        );

        //
        double price1 =
            isBullish
                ? _swings[0].low
                : _swings[0].high;
        datetime time1 = _swings[0].time;

        //
        double price2 =
            isBullish
                ? _swings[count - 1].low
                : _swings[count - 1].high;
        datetime time2 = _swings[count - 1].time;

        //
        ulong chartId = ChartIdentification();
        int subWindow = SubWindowIdentification();
        string name = ToXString(dir) + "_Trend_" + ToXString(price1) + "_" + ToXString(price2);
        RemoveObjects("_Trend_", chartId, subWindow);

        //
        object = new CChartObjectTrend();
        result = object.Create(
            chartId,
            name,
            subWindow,
            time1,
            price1,
            time2,
            price2 //
        );
        if (result)
        {
            //
            ApplyTrendLineStyle(
                object,
                dir //
            );

            //
            object.RayLeft(rayLeft);
            object.RayRight(rayRight);
        }

        //
        SpecifiedClean(_swings);

        //
        return result;
    }

    //
    // Stylers ...

    /**
     * Get Specified Style for Specified Direction ...
     *
     * @param  style: XPOIStyle, reference to hold result ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     */
    void GetStyle(
        XPOIStyle &style,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        // Prepare ...
        style.Clean();

        //
        // Validate ...
        if (!HasDirection(forDir))
        {
            return;
        }

        //
        if (IsSpecifiedBullish(forDir))
        {
            style = defaultBullishStyle;
        }
        else
        {
            style = defaultBearishStyle;
        }
    }

    /**
     * Get Specified Box Style for Specified Direction ...
     *
     * @param  style: XPOIStyle, reference to hold result ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     */
    void GetBoxStyle(
        XPOIStyle &style,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        // Prepare ...
        style.Clean();

        //
        // Validate ...
        if (!HasDirection(forDir))
        {
            return;
        }

        //
        if (IsSpecifiedBullish(forDir))
        {
            style = boxBullishStyle;
        }
        else
        {
            style = boxBearishStyle;
        }
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: XCBoxObject, pointer reference of Object ...
     * @param  bar: XOHCL, Specified Bar ...
     * @param  fill: boolean, Fill Bar's Body or not ...
     */
    void ApplyBarStyle(
        XCOHCLObject *&object,
        XOHCL &bar,
        bool fill = false //
    )
    {
        //
        // Validate ...
        bool has = bar.IsValid() &&
                   object != NULL;
        if (!has)
        {
            return;
        }

        //
        XPOIStyle style;
        GetStyle(
            style,
            bar.GetDirection() //
        );

        //
        object.BarSpes(
            style.width,
            style.clr,
            style.style,
            fill //
        );

        //
        style.Clean();
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: XCBoxObject, pointer reference of Object ...
     * @param  style: XPOIStyle, Specifed Style to Apply ...
     */
    void ApplyBoxStyle(
        XCBoxObject *&object,
        XPOIStyle &style //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        object.BoxFill(style.fill);
        object.BoxColor(style.clr);
        object.BoxWidth(style.width);
        object.BoxStyle(style.style);
    }

    /**
     * Apply Specified Style to a Collection on Drawn Boxes ...
     *
     * @param  objects: pointer reference of Objects ...
     * @param  style: XPOIStyle, Specifed Style to Apply ...
     */
    void ApplyBoxesStyle(
        CArrayObj *&objects,
        XPOIStyle &style //
    )
    {
        //
        // Validate ...
        if (objects == NULL || objects.Total() <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < objects.Total(); i++)
        {
            //
            XCBoxObject *iObj = ((XCBoxObject *)objects.At(i));
            ApplyBoxStyle(
                iObj,
                style //
            );
            ZeroMemory(iObj);
        }
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: XCBarArrowObject, pointer reference of Object ...
     * @param  style: XPOIStyle, Specifed Style to Apply ...
     */
    void ApplyBarArrowStyle(
        XCBarArrowObject *&object,
        XPOIStyle &style //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        object.ArrowColor(style.clr);
        object.ArrowWidth(style.width);
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: CChartObjectVLine, pointer reference of Object ...
     * @param  style: XPOIStyle, Specifed Style to Apply ...
     */
    void ApplyLineStyle(
        CChartObjectVLine *&object,
        XPOIStyle &style //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        object.Color(style.clr);
        object.Style(style.style);
        object.Width(style.width);
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: CChartObjectVLine, pointer reference of Object ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     */
    void ApplyLineStyle(
        CChartObjectVLine *&object,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        has = HasDirection(forDir);
        if (!has)
        {
            return;
        }

        //
        XPOIStyle style;
        GetStyle(style, forDir);

        //
        object.Color(style.clr);
        object.Style(style.style);
        object.Width(style.width);

        //
        style.Clean();
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: CChartObjectHLine, pointer reference of Object ...
     * @param  style: XPOIStyle, Specifed Style to Apply ...
     */
    void ApplyLineStyle(
        CChartObjectHLine *&object,
        XPOIStyle &style //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        object.Color(style.clr);
        object.Style(style.style);
        object.Width(style.width);
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: CChartObjectHLine, pointer reference of Object ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     */
    void ApplyLineStyle(
        CChartObjectHLine *&object,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        has = HasDirection(forDir);
        if (!has)
        {
            return;
        }

        //
        XPOIStyle style;
        GetStyle(style, forDir);

        //
        object.Color(style.clr);
        object.Style(style.style);
        object.Width(style.width);

        //
        style.Clean();
    }

    /**
     * Apply Specified Style to Specified Object ...
     *
     * @param  object: CChartObjectTrend, pointer reference of Object ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     */
    void ApplyTrendLineStyle(
        CChartObjectTrend *&object,
        ENUM_X_DIRECTION forDir //
    )
    {
        //
        bool has = object != NULL;
        if (!has)
        {
            return;
        }

        //
        if (!HasDirection(forDir))
        {
            return;
        }

        //
        XPOIStyle style;
        GetStyle(style, forDir);

        //
        object.Color(style.clr);
        object.Style(style.style);
        object.Width(style.width);

        //
        style.Clean();
    }

    //
    // SPecial Draws Functions ...

    /**
     * Draw Specified Pattern Pivot ...
     *
     * @param  _pivot: XPivot, reference to Specified Pivot ...
     * @param  config: XPOIDrawConfig, Darwing Configuration ...
     * @param  thresholdMultiplier: int, pivot point threshold ...
     */
    void DrawPivot(
        XPivot &_pivot,
        XPOIDrawConfig &config,
        int thresholdMultiplier = 0 //
    )
    {
        //
        // Normalize ...
        thresholdMultiplier = NormalizeInt(thresholdMultiplier, 1);

        //
        int arrowThreshold = config.arrowsThreshold * thresholdMultiplier;
        arrowThreshold = NormalizeInt(arrowThreshold, 1);

        //
        // Validate ...
        bool has = _pivot.IsValid();
        if (!has)
        {
            return;
        }

        //
        XOHCL _bar;
        has = _pivot.GetBar(_bar);
        if (!has)
        {
            //
            _bar.Clean();
            return;
        }

        //
        XCBarArrowObject *iObj;
        has = DrawBarArrow(
            _bar,
            iObj,
            _pivot.IsPeak()
                ? config.arrowsPeakPriceType
                : config.arrowsValePriceType,
            _pivot.IsBullish()
                ? config.bullishArrow
                : config.bearishArrow,
            _pivot.IsBullish()
                ? config.bullishColor
                : config.bearishColor,
            config.arrowsWidth,
            _pivot.IsBullish()
                ? ANCHOR_TOP
                : ANCHOR_BOTTOM,
            _pivot.prefix + "_Pivot_" + ToXString(TimeToSeconds(_pivot.time)),
            true,          // Ignore Bar Tag ...
            arrowThreshold // Threshold ...
        );
        if (has)
        {
            drawnObjects.Add(iObj);
        }

        //
        ZeroMemory(iObj);
    }

    /**
     * Draw Specified Pattern Zone ...
     *
     * @param  _zone: XBozxZone, reference to Specified Zone ...
     * @param  config: XPOIDrawConfig, Darwing Configuration ...
     */
    void DrawZone(
        XBoxZone &_zone,
        XPOIDrawConfig &config //
    )
    {
        //
        // Validate ...
        bool has = _zone.IsValid();
        if (!has)
        {
            return;
        }

        //
        XCBoxObject *iObj;
        has = DrawBox(
            _zone,
            iObj,
            config.ignoreZonesAt,
            _zone.type + "_Zone_" + ToXString(TimeToSeconds(_zone.from)) //
        );
        if (has)
        {
            //
            XPOIStyle iStyle;
            GetBoxStyle(
                iStyle,
                _zone.dir //
            );

            //
            iStyle.clr =
                _zone.IsBullish()
                    ? config.bullishColor
                    : config.bearishColor;
            iStyle.width = config.zonesWidth;
            iStyle.style = config.zonesStyle;

            //
            ApplyBoxStyle(
                iObj,
                iStyle //
            );

            //
            drawnObjects.Add(iObj);

            //
            iStyle.Clean();
        }

        //
        ZeroMemory(iObj);
    }

    /**
     * Draw Specified Zones ...
     *
     * @param  _zone: XBozxZone, reference to Collection Specified Zone ...
     * @param  config: XPOIDrawConfig, Darwing Configuration ...
     */
    void DrawZones(
        XBoxZone &_zones[],
        XPOIDrawConfig &config //
    )
    {
        //
        // Validate ...
        bool has = HasChild(_zones);
        if (!has)
        {
            return;
        }

        //
        for (int i = 0; i < ArraySize(_zones); i++)
        {
            //
            DrawZone(
                _zones[i],
                config //
            );
        }
    }

    /**
     * Draw Pivots ...
     *
     * @param  pivots: XPivot, collection reference to Draw ...
     * @param  config: XPOIDrawConfig, reference to Specified Draw Style ...
     */
    void DrawPivots(
        XPivot &pivots[],
        XPOIDrawConfig &config //
    )
    {
        //
        // Validate ...
        bool has = HasChild(pivots);
        if (!has)
        {
            return;
        }

        //
        XPivot tmps[];
        int count = ArraySize(pivots);
        for (int i = 0; i < count; i++)
        {
            //
            XClean(tmps);
            int sameCounts = ExtractSameTimePivots(pivots[i], tmps, pivots);
            has = IsValidSize(sameCounts);
            if (has)
            {
                //
                for (int j = 0; j < sameCounts; j++)
                {
                    DrawPivot(tmps[j], config, j + 1);
                }
            }
        }

        //
        // Cleanup ...
        XClean(tmps);
    }

    void DrawTrendLine(
        string name,
        double fromPrice,
        datetime fromTime,
        double toPrice,
        datetime toTime,
        XPOIDrawConfig &config,
        color clr = CLR_NONE //
    )
    {
        //
        // Validate ...
        bool has =
            IsXValid(name) &&
            IsXValid(toTime) &&
            IsXValid(fromTime) &&
            NotEmptyZero(toPrice) &&
            NotEmptyZero(fromPrice) &&
            fromTime < toTime;
        if (!has)
        {
            return;
        }

        //
        ENUM_X_DIRECTION trendDir =
            fromPrice > toPrice
                ? X_DIRECTION_BEARISH
            : fromPrice < toPrice
                ? X_DIRECTION_BULLISH
                : X_DIRECTION_NONE;
        color clrTrend =
            (clr != CLR_NONE)
                ? clr
            : IsXBullish(trendDir)
                ? config.bullishColor
                : config.bearishColor;

        //
        CChartObjectTrend *object;
        has = DrawTrendLine(
            fromPrice,
            fromTime,
            toPrice,
            toTime,
            object,
            name,
            config.trendRayRight,
            config.trendRayLeft //
        );
        if (has)
        {
            //
            // Apply Style ...
            object.Color(clrTrend);
            object.Width(config.trendWidth);
            object.Style(config.trendStyle);

            //
            // Save Object ...
            drawnObjects.Add(object);
        }

        //
        ZeroMemory(object);
    }

    void DrawTrendLine(
        XTrend &trend,
        XPOIDrawConfig &config,
        color clr = CLR_NONE //
    )
    {
        //
        // Validate ...
        bool has = trend.IsValid();
        if (!has)
        {
            return;
        }

        //
        color trendColor =
            (clr != CLR_NONE)
                ? clr
            : trend.IsBullish()
                ? config.bullishColor
            : trend.IsBearish()
                ? config.bearishColor
                : config.neuturalColor;
        string name = trend.GetObjectID();

        //
        CChartObjectTrend *object;
        has = DrawTrendLine(
            trend.fromValue,
            trend.fromTime,
            trend.toValue,
            trend.toTime,
            object,
            name,
            config.trendRayRight,
            config.trendRayLeft //
        );
        if (has)
        {
            //
            // Apply Style ...
            object.Color(trendColor);
            object.Width(config.trendWidth);
            object.Style(config.trendStyle);

            //
            // Save Object ...
            drawnObjects.Add(object);
        }

        //
        ZeroMemory(object);
    }

    /**
     * Draw Specified Pattern info ...
     *
     * @param  _bar: XOHCL, reference to Pattern's Bar ...
     * @param  _pivot: XPivot, reference to Pattern's Pivot ...
     * @param  _zone: XBoxZone, reference to Pattern's Zone ...
     * @param  _pattern: ENUM_X_BAR_PATTERN, Specified Bar Pattern ...
     * @param  _patternDir: ENUM_X_DIRECTION, Specified Bar Pattern's Direction ...
     * @param  thresholdMultiplier: int, pivot point threshold ...
     * @param  config: XPOIDrawConfig, Darwing Configuration ...
     */
    void DrawPattern(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_BAR_PATTERN _pattern,
        ENUM_X_DIRECTION _patternDir,
        XPOIDrawConfig &config,
        int thresholdMultiplier = 0 //
    )
    {
        //
        // Validate ...
        bool has =
            _bar.IsValid() &&
            IsXValid(_pattern) &&
            HasDirection(_patternDir);
        if (!has)
        {
            return;
        }

        //
        datetime cTime = TimeCurrent();

        //
        // Handle Draws ...

        //
        // Draw OHCL Bar ...

        //
        // Draw Pivot ...
        DrawPivot(_pivot, config, thresholdMultiplier);

        //
        // Draw Zone ...
        _zone.to = cTime;
        DrawZone(_zone, config);
    }

    /**
     * Draw Specified Bar Pattern Analysis ...
     *
     * @param  analysis: XPatternAnalysis, reference to Specified Bar Pattern Analysis ...
     * @param  config: XPatternAnalysisDrawConfig, reference to Provide Drawing Configuration ...
     */
    void DrawPatternAnalysis(
        XPatternAnalysis &analysis,
        XPatternAnalysisDrawConfig &config //
    )
    {
        //
        bool has = analysis.IsValid();
        if (!has)
        {
            return;
        }

        //
        bool canDraw;
        bool isBullish;
        bool isBearish;
        XOHCL patternBar;
        bool canDrawZone;
        bool canDrawPivot;
        XPivot patternPivot;
        XBoxZone patternBox;
        ENUM_X_BAR_PATTERN pattern;
        ENUM_X_DIRECTION patternDir;

        //
        datetime cTime = TimeCurrent();
        int patternsPivotThresholdMultiplier = 0;

        //
        // Support and Resistance ...

        //
        // Support ...
        isBearish = false;
        isBullish = analysis.IsSupport();
        has = isBullish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_SUPPORT;
            patternDir = X_DIRECTION_BULLISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Resistance ...
        isBullish = false;
        isBearish = analysis.IsResistance();
        has = isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_RESISTANCE;
            patternDir = X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // PV: High and Low ...

        //
        // Low ...
        isBearish = false;
        isBullish = analysis.IsLow();
        has = isBullish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_LOW;
            patternDir = X_DIRECTION_BULLISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // High ...
        isBullish = false;
        isBearish = analysis.IsHigh();
        has = isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_HIGH;
            patternDir = X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // OB ...
        isBullish = analysis.IsOB(X_DIRECTION_BULLISH);
        isBearish = analysis.IsOB(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_OB;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // FVG ...
        isBullish = analysis.IsFVG(X_DIRECTION_BULLISH);
        isBearish = analysis.IsFVG(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_FVG;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Momentum ...
        isBullish = analysis.IsMomentum(X_DIRECTION_BULLISH);
        isBearish = analysis.IsMomentum(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_MOMENTUM;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Engulfed ...
        isBullish = analysis.IsEngulfed(X_DIRECTION_BULLISH);
        isBearish = analysis.IsEngulfed(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_ENGULFED;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Pinned ...
        isBullish = analysis.IsPinned(X_DIRECTION_BULLISH);
        isBearish = analysis.IsPinned(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_PINNED;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // TrueGaped ...
        isBullish = analysis.IsTrueGaped(X_DIRECTION_BULLISH);
        isBearish = analysis.IsTrueGaped(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_TRUE_GAPED;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Star ...
        isBullish = analysis.IsStar(X_DIRECTION_BULLISH);
        isBearish = analysis.IsStar(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_STAR;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Piercing ...
        isBullish = analysis.IsPiercing(X_DIRECTION_BULLISH);
        isBearish = analysis.IsPiercing(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_PIERCING;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Rising ...
        isBullish = analysis.IsRising(X_DIRECTION_BULLISH);
        isBearish = analysis.IsRising(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_RISING;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Flag ...
        isBullish = analysis.IsFlag(X_DIRECTION_BULLISH);
        isBearish = analysis.IsFlag(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_FLAG;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // SignalKey ...
        isBullish = analysis.IsSignalKey(X_DIRECTION_BULLISH);
        isBearish = analysis.IsSignalKey(X_DIRECTION_BEARISH);
        has = isBullish || isBearish;
        if (has)
        {
            //
            pattern = X_BAR_PATTERN_SIGNALKEY;
            patternDir =
                isBullish
                    ? X_DIRECTION_BULLISH
                    : X_DIRECTION_BEARISH;
            canDraw = config.CanDraw(pattern);
            if (canDraw)
            {
                //
                has = analysis.GetPattern(
                    patternBar,
                    patternPivot,
                    patternBox,
                    pattern,
                    patternDir //
                );
                if (has)
                {
                    //
                    canDrawZone = config.CanDrawZone(pattern);
                    if (!canDrawZone)
                    {
                        patternBox.Clean();
                    }

                    //
                    canDrawPivot = config.CanDrawPivot(pattern);
                    if (!canDrawPivot)
                    {
                        patternPivot.Clean();
                    }
                    else
                    {
                        patternsPivotThresholdMultiplier++;
                    }

                    //
                    DrawPattern(
                        patternBar,
                        patternPivot,
                        patternBox,
                        pattern,
                        patternDir,
                        config.drawConfig,
                        patternsPivotThresholdMultiplier //
                    );
                }
            }
        }

        //
        // Cleanup Resources ...
        patternBar.Clean();
        patternBox.Clean();
        patternPivot.Clean();
    }

    /**
     * Draw a Collection of Pattern Analysis ...
     *
     * @param  analysises: XPatternAnalysis, collection reference to Data Source ...
     * @param  config: XPatternAnalysisDrawConfig, reference to Specified Drawing Config ...
     */
    void DrawPatternAnalysises(
        XPatternAnalysis &analysises[],
        XPatternAnalysisDrawConfig &config //
    )
    {
        //
        bool has = HasChild(analysises);
        if (!has)
        {
            return;
        }

        //
        int count = ArraySize(analysises);
        for (int i = 0; i < count; i++)
        {
            DrawPatternAnalysis(analysises[i], config);
        }
    }

    /**
     * Draw Specified Pivots Analysis ...
     *
     * @param  analysis: XPivotAnalysis, reference to Specified Pivot Analysis ...
     * @param  config: XPivotAnalysisDrawConfig, reference to Provide Drawing Configuration ...
     */
    void DrawPivotAnalysis(
        XPivotAnalysis &analysis,
        XPivotAnalysisDrawConfig &config //
    )
    {
        //
        bool has = analysis.IsValid();
        if (!has)
        {
            return;
        }

        //
        int count = 0;
        XBoxZone tmpBox;
        datetime cTime = TimeCurrent();

        //
        // Pivots ...
        if (config.drawPivots)
        {
            //
            has = analysis.HasPivots();
            if (has)
            {
                //
                count = ArraySize(analysis.pivots);
                for (int i = 0; i < count; i++)
                {
                    //
                    DrawPivot(
                        analysis.pivots[i],
                        config.drawConfig //
                    );
                }
            }
        }

        //
        // Support ...
        if (config.drawSupport)
        {
            //
            tmpBox.Clean();
            has = analysis.HasSupportBox(tmpBox);
            if (has)
            {
                //
                tmpBox.to = cTime;
                DrawZone(
                    tmpBox,
                    config.drawConfig //
                );
            }
        }

        //
        // Resistance ...
        if (config.drawResistance)
        {
            //
            tmpBox.Clean();
            has = analysis.HasResistanceBox(tmpBox);
            if (has)
            {
                //
                tmpBox.to = cTime;
                DrawZone(
                    tmpBox,
                    config.drawConfig //
                );
            }
        }

        //
        // Peak Trend ...
        if (config.drawPeakTrend)
        {
            //
            has = analysis.IsPeakAnalysed();
            if (has)
            {
                //
                string iUpperName = analysis.GetUpperObjectID();
                CChartObjectTrend *iObj = new CChartObjectTrend();
                has = iObj.Create(
                    ChartIdentification(),
                    iUpperName,
                    SubWindowIdentification(),
                    analysis.fromTime,
                    analysis.peakFrom,
                    analysis.toTime,
                    analysis.peakTo //
                );
                if (has)
                {
                    //
                    // Styling ...
                    iObj.RayLeft(config.rayLeftTrends);
                    iObj.RayRight(config.rayRightTrends);
                    iObj.Width(config.drawConfig.zonesWidth);
                    iObj.Style(config.drawConfig.zonesStyle);
                    iObj.Color(IsXBullish(analysis.peakDir)
                                   ? config.drawConfig.bullishColor
                                   : config.drawConfig.bearishColor);

                    //
                    // Storing ...
                    drawnObjects.Add(iObj);
                }
            }
        }

        //
        // Vale Trend ...
        if (config.drawValeTrend)
        {
            //
            has = analysis.IsValeAnalysed();
            if (has)
            {
                //
                string iLowerName = analysis.GetLowerObjectID();
                CChartObjectTrend *iObj = new CChartObjectTrend();
                has = iObj.Create(
                    ChartIdentification(),
                    iLowerName,
                    SubWindowIdentification(),
                    analysis.fromTime,
                    analysis.valeFrom,
                    analysis.toTime,
                    analysis.valeTo //
                );
                if (has)
                {
                    //
                    // Styling ...
                    iObj.RayLeft(config.rayLeftTrends);
                    iObj.RayRight(config.rayRightTrends);
                    iObj.Width(config.drawConfig.zonesWidth);
                    iObj.Style(config.drawConfig.zonesStyle);
                    iObj.Color(IsXBullish(analysis.valeDir)
                                   ? config.drawConfig.bullishColor
                                   : config.drawConfig.bearishColor);

                    //
                    // Storing ...
                    drawnObjects.Add(iObj);
                }
            }
        }

        //
        // Cleanup ...
        tmpBox.Clean();

        //
    }

  protected:
    //

    //
  private:
    //

    //
    // Props ...

    //
    string mPrefix;               // Prefix of Created Objects ...
    long mChartIdentification;    // Chart Id ...
    int mSubWindowIdentification; // Sub Window ...

    //
};

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-position.drawer.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCPositionDrawer
// Description: Provides Position
// Drawing Tools ...
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-cobject.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Libraries/x-saherelm.x-pivot.analysis.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"

//
// Definitions ...

//
// Implementation ...

class XCPositionDrawer : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...
    int window;                 // Window ID of Chart ...
    ulong chartID;              // Chart ID ...
    XCTrade *trader;            // XCTrade Instance ...
    XPOIStyle riskStyle;        // Risk Box Style ...
    color inProfitColor;        // In Profit Price Box Color ...
    color inDrawdownColor;      // In Drawdown Price Box Color ...
    ENUM_LINE_STYLE priceStyle; // Price Box Style ...
    XPOIStyle rewardStyle;      // Reward Box Style ...
    CArrayObj positionObjects;  // Drawn Positions Object Instances Collection ...
    CArrayObj finalizedObjects; // Finalized Drawn Positions Object Instances Collection ...

    //
    // Constructor ...
    XCPositionDrawer(
        XCTrade *_trader,
        ulong _chartID = 0,
        int _window = 0 //
        ) : XCBase()
    {
        //
        Init(
            _trader,
            _chartID,
            _window //
        );
    }

    //
    // Deconstructor ...
    ~XCPositionDrawer()
    {
        Destroy();
    }

    //
    // Tools ...

    /**
     * Initialize Class Instance ...
     *
     * @param  _trader: XCTrade, reference Pointer to Trader Instance Class ...
     * @param  _chartID: ulong, Specified Chart ID ...
     * @param  _window: int, Specified Chart Window ID ...
     */
    void Init(
        XCTrade *_trader,
        ulong _chartID = 0,
        int _window = 0 //
    )
    {
        //
        // Validate ...
        bool has =
            _window >= 0 &&
            _chartID >= 0 &&
            _trader != NULL;
        if (!has)
        {
            return;
        }

        //
        trader = _trader;
        window = _window;
        chartID = _chartID;

        //
        // Prepare Default Styles ...

        //
        // Risk Style ...
        riskStyle.width = 1;
        riskStyle.fill = false;
        riskStyle.clr = clrRed;
        riskStyle.style = STYLE_DASH;

        //
        // Reward Style ...
        rewardStyle.width = 1;
        rewardStyle.fill = false;
        rewardStyle.clr = clrLime;
        rewardStyle.style = STYLE_DASH;

        //
        // Price Style ...
        priceStyle = STYLE_SOLID;
        inProfitColor = clrAqua; // ApplyAlpha(clrAqua, 100);
        inDrawdownColor = clrMagenta; // ApplyAlpha(clrMagenta, 100);
    }

    /**
     * Destroy Instance ...
     */
    void Destroy()
    {
        //
        riskStyle.Clean();
        rewardStyle.Clean();
        // positionObjects
        // finalizedObjects
    }

    /**
     * Validate Instance ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            trader != NULL;

        //
        return result;
    }

    /**
     * Update Positions ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     */
    void Update(int barIndex = 0)
    {
        //
        // Normalize ...
        barIndex = NormalizeInt(barIndex, 0);

        //
        // Validate ...
        bool has = IsValid();
        if (!has)
        {
            return;
        }

        //
        // Update Exists Positions ...
        XPosition positions[];
        int positionsCount = trader.GetPositions(positions);
        has = IsValidSize(positionsCount);
        if (has)
        {
            //
            int drawingsCount = positionObjects.Total();
            has = IsValidSize(drawingsCount);
            if (has)
            {
                //
                for (int i = 0; i < positionsCount; i++)
                {
                    //
                    for (int j = 0; j < drawingsCount; j++)
                    {
                        //
                        // Check Object is Belong to Position ...
                        has = ((XCPositionObject *)positionObjects.At(j)).IsBelong(positions[i]);
                        if (has)
                        {
                            //
                            // Update Position Object ...
                            has = ((XCPositionObject *)positionObjects.At(j)).Update(positions[i]);
                        }
                    }
                }
            }
        }

        //
        // Update Test Positions ...
        positionsCount = positionObjects.Total();
        has =
            !HasChild(positions) &&
            IsValidSize(positionsCount);
        if (has)
        {
            //
            XPosition iPosition;
            for (int i = 0; i < positionsCount; i++)
            {
                //
                // Retrieve Position from Object ...
                has = ((XCPositionObject *)positionObjects.At(i)).GetPosition(iPosition);
                if (has)
                {
                    //
                    // Manully Update Position ...
                    has = ((XCPositionObject *)positionObjects.At(i)).Update(barIndex);
                }
            }

            //
            iPosition.Clean();
        }
    }

    //
    // Trade Event Handlers ...

    /**
     * Add Executed Signal ...
     *
     * @param  signal: XSignal, reference to Executed Signal ...
     */
    void AddExecutedSignal(XSignal &signal)
    {
        //
        // Validate ...
        bool has =
            signal.IsValid() &&
            signal.IsExecuted();
        if (!has)
        {
            return;
        }

        //
        XPosition iPosition;

        //
        // Check Position Exists ...
        has = trader.GetPosition(
            signal.positionId,
            iPosition //
        );
        //
        // Initial by Testing Position ...
        if (!has)
        {
            //
            has = ToXPosition(
                signal,
                iPosition,
                trader.GetMagicNumber(),
                signal.positionId //
            );
        }

        //
        // Validate result ...
        if (has)
        {
            //
            XCPositionObject *iObj = new XCPositionObject();
            has = iObj.Create(
                chartID,
                window,
                iPosition //
            );
            if (has)
            {
                //
                // Set Stylers to Position ...
                iObj.riskStyle = riskStyle;
                iObj.priceStyle = priceStyle;
                iObj.rewardStyle = rewardStyle;
                iObj.inProfitColor = inProfitColor;
                iObj.inDrawdownColor = inDrawdownColor;

                //
                // Add to Collection ...
                positionObjects.Add(iObj);
            }
        }

        //
        // Cleanup Resources ...
        iPosition.Clean();
    }

    /**
     * Handle Stop Loss Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleStopLossTriggered(const XDeal &deal)
    {
        //
        Finalize(deal.positionId);
    }

    /**
     * Handle Take Profit Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleOnTakeProfitTriggered(const XDeal &deal)
    {
        Finalize(deal.positionId);
    }

    /**
     * Handle Position Force Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param position: XPosition instance refrence, Triggered Position ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionForceClosed(
        const ulong ticket,
        const XPosition &position,
        const string comment //
    )
    {
        Finalize(ticket);
    }

    /**
     * Handle Position Modified Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param profit: double, Position Profit ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionModified(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
        //
        // Detect Position Index ...
        int index = FindIndex(ticket);
        bool has = IsValidIndex(index);
        if (!has)
        {
            return;
        }

        //
        // Get Position of Objects ...
        XPosition _position;
        has = ((XCPositionObject *)positionObjects.At(index)).GetPosition(_position);
        if (has)
        {
            //
            // Get Position of Trader Instance ...
            has = trader.GetPosition(
                ticket,
                _position //
            );
            if (has)
            {
                //
                // Update Position ...
                ((XCPositionObject *)positionObjects.At(index)).Update(_position);
            }
        }

        //
        // Cleanup Resources ...
        _position.Clean();
    }

    //
    // Protected ...
  protected:
    //

    /**
     * Finalize Specified Position ...
     * Remove it From Position Objects and add it to Finalized Objects Cllection ...
     *
     * @param  ticket: ulong, Specified Position Ticket ...
     */
    void Finalize(ulong ticket)
    {
        //
        int index = FindIndex(ticket);
        bool has = IsValidIndex(index);
        if (!has)
        {
            return;
        }

        //
        // XCPositionObject *iObj = positionObjects.At(index);
        // positionObjects.Delete(index);
        // finalizedObjects.Add(iObj);
    }

    /**
     * Find Specified Position Index ...
     *
     * @param  ticket: ulong, Specified Position Ticket ...
     *
     * @return ( int )
     */
    int FindIndex(ulong ticket)
    {
        //
        int result = -1;

        //
        // Validate ...
        int count =
            NotEmptyZero(ticket) &&
            positionObjects.Total();
        bool has = IsValidSize(count);
        if (!has)
        {
            return result;
        }

        //
        XPosition iPosition;
        for (int i = 0; i < count; i++)
        {
            //
            has = ((XCPositionObject *)positionObjects.At(i)).GetPosition(iPosition);
            has = has &&
                  iPosition.ticket == ticket;
            if (has)
            {
                //
                result = i;
                break;
            }
        }

        //
        // Cleanup Resources ...
        iPosition.Clean();

        //
        return result;
    }

    //
    // Private ...
  private:
    //

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-restrictions.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCRestrictions
// Description: provides all Requirements for Apply Restrictions ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Libraries/x-saherelm.x-trade.lib.mq5"

// #region Definitions ...
//
// Awailable Restrictions Period ...
enum ENUM_X_TRADE_RESTRICTIONS_PERIOD
{
    X_TRADE_RESTRICATION_NONE,    // None
    X_TRADE_RESTRICATION_HOURLY,  // Per Hour
    X_TRADE_RESTRICATION_DAILY,   // Per Day
    X_TRADE_RESTRICATION_WEEKLY,  // Per Week
    X_TRADE_RESTRICATION_MONTHLY, // Per Month
};

// #region Extensions ...
//
// Converts to String ...
string ToXString(ENUM_X_TRADE_RESTRICTIONS_PERIOD value)
{
    //
    string result = NULL;

    //
    result = EnumToString(value);

    //
    return result;
}

//
// Check Has Restrictions or not ...
bool HasXRestrictions(ENUM_X_TRADE_RESTRICTIONS_PERIOD value)
{
    //
    bool result = false;

    //
    result = value != X_TRADE_RESTRICATION_NONE;

    //
    return result;
}

/**
 * Get Restrictions Period Delay in Seconds ...
 *
 * @param  value: ENUM_X_TRADE_RESTRICTIONS_PERIOD ...
 *
 * @return ( int )
 */
int RestrictionsPeriodSeconds(ENUM_X_TRADE_RESTRICTIONS_PERIOD value)
{
    //
    int result = 0;

    //
    // Check Restrictions is Valid or not ...
    if (!HasXRestrictions(value))
    {
        return result;
    }

    //
    switch (value)
    {
    //
    default:
    case X_TRADE_RESTRICATION_NONE:
        result = 0;
        break;

    //
    case X_TRADE_RESTRICATION_HOURLY:
        result = PeriodSeconds(PERIOD_H1);
        break;

    //
    case X_TRADE_RESTRICATION_DAILY:
        result = PeriodSeconds(PERIOD_D1);
        break;

    //
    case X_TRADE_RESTRICATION_WEEKLY:
        result = PeriodSeconds(PERIOD_W1);
        break;

    //
    case X_TRADE_RESTRICATION_MONTHLY:
        result = PeriodSeconds(PERIOD_MN1);
        break;
    }

    //
    return result;
}

/**
 * Calculate Restrictions End Time ...
 *
 * @param  value: ENUM_X_TRADE_RESTRICTIONS_PERIOD ...
 * @param  multiplier: int, Restrictions Period Multiplier ...
 * @param  fromTime: Restrictions Starts Time ...
 *
 * @return ( datetime )
 */
datetime RestrictionsEndTime(
    ENUM_X_TRADE_RESTRICTIONS_PERIOD value,
    int multiplier = 1,
    datetime fromTime = NULL //
)
{
    //
    datetime result = NULL;

    //
    // Normalize Multiplier ...
    if (multiplier < 1)
    {
        multiplier = 1;
    }
    if (!IsXValid(fromTime))
    {
        fromTime = TimeCurrent();
    }

    //
    // Calculate Requirements ...
    int periodSeconds = RestrictionsPeriodSeconds(value);
    int timeInSeconds = (int)fromTime + (periodSeconds * multiplier);
    MqlDateTime timeStruct;
    bool hasStruct = TimeToStruct(
        (datetime)timeInSeconds,
        timeStruct //
    );
    if (hasStruct)
    {
        //
        timeStruct.min = 0;
        timeStruct.sec = 0;

        //
        result = StructToTime(timeStruct);
    }
    else
    {
        result = (datetime)timeInSeconds;
    }

    //
    ZeroMemory(timeStruct);

    //
    return result;
}
// #endregion

// #region Event Handler Types ...
typedef void (*TRestrictionEvent)();
// #endregion
// #endregion

//
// Implementation ...
class XCRestrictions : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructor ...
    XCRestrictions()
    {
        //
        Clean();
        Default();
    }

    //
    // Deconstructor ...
    ~XCRestrictions()
    {
        //
        Clean();
    }

    // #region Properties ...
    // #region RestrictionsPeriod ...
    /**
     * Get Restrictions Period ...
     *
     * @return ( ENUM_X_TRADE_RESTRICTIONS_PERIOD )
     */
    ENUM_X_TRADE_RESTRICTIONS_PERIOD GetRestrictionsPeriod()
    {
        return mRestrictionsPeriod;
    }

    /**
     * Get Restrictions Period ...
     *
     * @param  value: ENUM_X_TRADE_RESTRICTIONS_PERIOD
     */
    void SetRestrictionsPeriod(ENUM_X_TRADE_RESTRICTIONS_PERIOD value)
    {
        mRestrictionsPeriod = value;
    }
    // #endregion

    // #region Restrictions Period Multiplier ...
    /**
     * Get Restrictions Period Multiplier ...
     *
     * @return ( int )
     */
    int GetRestrictionsPeriodMultiplier()
    {
        return mRestrictionsPeriodMultiplier;
    }

    /**
     * Set Restrictions Period Multiplier ...
     *
     * @param  value: int ...
     */
    void SetRestrictionsPeriodMultiplier(int value)
    {
        //
        if (value < 1)
        {
            value = 1;
        }

        //
        mRestrictionsPeriodMultiplier = value;
    }
    // #endregion

    // #region Signals ...
    /**
     * Get Issued Signals ...
     *
     * @return ( int )
     */
    int GetSignals()
    {
        return mSignals;
    }

    /**
     * Get Issued Long Signals ...
     *
     * @return ( int )
     */
    int GetLongSignals()
    {
        return mLongSignals;
    }

    /**
     * Issued Short Signals ...
     *
     * @return ( int )
     */
    int GetShortSignals()
    {
        return mShortSignals;
    }

    /**
     * Get Issued Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedSignals()
    {
        return mRestrictedSignals;
    }

    /**
     * Get Issued Long Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedLongSignals()
    {
        return mRestrictedLongSignals;
    }

    /**
     * Get Issued Short Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedShortSignals()
    {
        return mRestrictedShortSignals;
    }

    /**
     * Get Allowed Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedSignalsInRestrictions()
    {
        return mAllowedSignalsInRestrictions;
    }

    /**
     * Set Allowed Signals in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedSignalsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedSignalsInRestrictions = value;
    }

    /**
     * Get Allowed Long Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedLongSignalsInRestrictions()
    {
        return mAllowedLongSignalsInRestrictions;
    }

    /**
     * Set Allowed Long Signals in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedLongSignalsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedLongSignalsInRestrictions = value;
    }

    /**
     * Get Allowed Short Signals in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedShortSignalsInRestrictions()
    {
        return mAllowedShortSignalsInRestrictions;
    }

    /**
     * Set Allowed Short Signals in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedShortSignalsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedShortSignalsInRestrictions = value;
    }
    // #endregion

    // #region Trades ...
    /**
     * Executed Trades ...
     *
     * @return ( int )
     */
    int GetTrades()
    {
        return mTrades;
    }

    /**
     * Executed Long Trades ...
     *
     * @return ( int )
     */
    int GetLongTrades()
    {
        return mLongTrades;
    }

    /**
     * Executed Short Trades ...
     *
     * @return ( int )
     */
    int GetShortTrades()
    {
        return mShortTrades;
    }

    /**
     * Executed Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedTrades()
    {
        return mRestrictedTrades;
    }

    /**
     * Executed Long Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedLongTrades()
    {
        return mRestrictedLongTrades;
    }

    /**
     * Executed Short Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedShortTrades()
    {
        return mRestrictedShortTrades;
    }

    /**
     * Get Allowed Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedTradesInRestrictions()
    {
        return mAllowedTradesInRestrictions;
    }

    /**
     * Set Allowed Trades in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedTradesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedTradesInRestrictions = value;
    }

    /**
     * Get Allowed Long Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedLongTradesInRestrictions()
    {
        return mAllowedLongTradesInRestrictions;
    }

    /**
     * Set Allowed Long Trades in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedLongTradesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedLongTradesInRestrictions = value;
    }

    /**
     * Get Allowed Short Trades in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedShortTradesInRestrictions()
    {
        return mAllowedShortTradesInRestrictions;
    }

    /**
     * Set Allowed Short Trades in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedShortTradesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedShortTradesInRestrictions = value;
    }
    // #endregion

    // #region Wins ...
    /**
     * Touched Wins ...
     *
     * @return ( int )
     */
    int GetWins()
    {
        return mWins;
    }

    /**
     * Touched Long Wins ...
     *
     * @return ( int )
     */
    int GetLongWins()
    {
        return mLongWins;
    }

    /**
     * Touched Short Wins ...
     *
     * @return ( int )
     */
    int GetShortWins()
    {
        return mShortWins;
    }

    /**
     * Touched Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedWins()
    {
        return mRestrictedWins;
    }

    /**
     * Touched Long Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedLongWins()
    {
        return mRestrictedLongWins;
    }

    /**
     * Touched Short Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedShortWins()
    {
        return mRestrictedShortWins;
    }

    /**
     * Get Allowed Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedWinsInRestrictions()
    {
        return mAllowedWinsInRestrictions;
    }

    /**
     * Set Allowed Wins in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedWinsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedWinsInRestrictions = value;
    }

    /**
     * Get Allowed Long Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedLongWinsInRestrictions()
    {
        return mAllowedLongWinsInRestrictions;
    }

    /**
     * Set Allowed Long Wins in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedLongWinsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedLongWinsInRestrictions = value;
    }

    /**
     * Get Allowed Short Wins in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedShortWinsInRestrictions()
    {
        return mAllowedShortWinsInRestrictions;
    }

    /**
     * Set Allowed Short Wins in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedShortWinsInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedShortWinsInRestrictions = value;
    }
    // #endregion

    // #region Losess ...
    /**
     * Touched Loses ...
     *
     * @return ( int )
     */
    int GetLoses()
    {
        return mLoses;
    }

    /**
     * Touched Long Loses ...
     *
     * @return ( int )
     */
    int GetLongLoses()
    {
        return mLongLoses;
    }

    /**
     * Touched Short Loses ...
     *
     * @return ( int )
     */
    int GetShortLoses()
    {
        return mShortLoses;
    }

    /**
     * Touched Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedLoses()
    {
        return mRestrictedLoses;
    }

    /**
     * Touched Long Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedLongLoses()
    {
        return mRestrictedLongLoses;
    }

    /**
     * Touched Short Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetRestrictedShortLoses()
    {
        return mRestrictedShortLoses;
    }

    /**
     * Get Allowed Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedLosesInRestrictions()
    {
        return mAllowedLosesInRestrictions;
    }

    /**
     * Set Allowed Loses in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedLosesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedLosesInRestrictions = value;
    }

    /**
     * Get Allowed Long Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedLongLosesInRestrictions()
    {
        return mAllowedLongLosesInRestrictions;
    }

    /**
     * Set Allowed Long Loses in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedLongLosesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedLongLosesInRestrictions = value;
    }

    /**
     * Get Allowed Short Loses in Restrictions Period ...
     *
     * @return ( int )
     */
    int GetAllowedShortLosesInRestrictions()
    {
        return mAllowedShortLosesInRestrictions;
    }

    /**
     * Set Allowed Short Loses in Restrictions Period ...
     *
     * @param  value: int ...
     */
    void SetAllowedShortLosesInRestrictions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mAllowedShortLosesInRestrictions = value;
    }
    // #endregion

    // #region Current Restrictions ...
    /**
     * Get Current Restrictions End Time ...
     *
     * @return ( datetime )
     */
    datetime GetCurrentRestrictionsEnd()
    {
        return mRestrictionsEnd;
    }

    /**
     * Get Current Restrictions Start Time ...
     *
     * @return ( datetime )
     */
    datetime GetCurrentRestrictionsStart()
    {
        return mRestrictionsStart;
    }
    // #endregion
    // #endregion

    // #region Tools ...
    /**
     * Check Restrictions Exists or not ...
     *
     * @return ( bool )
     */
    bool HasRestrictions()
    {
        //
        bool result = false;

        //
        result =
            HasXRestrictions(mRestrictionsPeriod);

        //
        return result;
    }

    /**
     * Handle Signal Issued ...
     *
     * @param  signal: XSignal ...
     */
    void HandleSignal(XSignal &signal)
    {
        //
        if (!signal.IsValid())
        {
            return;
        }

        //
        bool isLong = IsLong(signal.type);

        //
        mSignals++;

        //
        if (isLong)
        {
            mLongSignals++;
        }
        else
        {
            mShortSignals++;
        }

        //
        if (HasRestrictions())
        {
            //
            mRestrictedSignals++;

            //
            if (isLong)
            {
                mRestrictedLongSignals++;
            }
            else
            {
                mRestrictedShortSignals++;
            }
        }
    }

    /**
     * Handle Signal Execution ...
     *
     * @param  signal: XSignal ...
     */
    void HandleTrade(XSignal &signal)
    {
        //
        if (!signal.IsValid())
        {
            return;
        }
        bool isLong = IsLong(signal.type);

        //
        mTrades++;

        //
        if (isLong)
        {
            mLongTrades++;
        }
        else
        {
            mShortTrades++;
        }

        //
        if (HasRestrictions())
        {
            //
            mRestrictedTrades++;

            //
            if (isLong)
            {
                mRestrictedLongTrades++;
            }
            else
            {
                mRestrictedShortTrades++;
            }
        }
    }

    /**
     * Handle Trade Win ...
     *
     * @param  type: ENUM_X_POSITION_TYPES ...
     */
    void HandleTradeWin(ENUM_X_POSITION_TYPES type)
    {
        //
        if (!IsXValid(type))
        {
            return;
        }
        bool isLong = IsLong(type);

        //
        mWins++;

        //
        if (isLong)
        {
            mLongWins++;
        }
        else
        {
            mShortWins++;
        }

        //
        if (HasRestrictions())
        {
            //
            mRestrictedWins++;

            //
            if (isLong)
            {
                mRestrictedLongWins++;
            }
            else
            {
                mRestrictedShortWins++;
            }
        }
    }

    /**
     * Handle Trade Lose ...
     *
     * @param  type: ENUM_X_POSITION_TYPES ...
     */
    void HandleTradeLose(ENUM_X_POSITION_TYPES type)
    {
        //
        if (!IsXValid(type))
        {
            return;
        }
        bool isLong = IsLong(type);

        //
        mLoses++;

        //
        if (isLong)
        {
            mLongLoses++;
        }
        else
        {
            mShortLoses++;
        }

        //
        if (HasRestrictions())
        {
            //
            mRestrictedLoses++;

            //
            if (isLong)
            {
                mRestrictedLongLoses++;
            }
            else
            {
                mRestrictedShortLoses++;
            }
        }
    }

    /**
     * Check Allow Issue Signal or not ...
     *
     * @param  signal: XSignal ...
     *
     * @return ( bool )
     */
    bool CheckAllowIssueSignal(XSignal &signal)
    {
        //
        bool result = false;

        //
        // Validate Signal ...
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }
        bool isLong = IsLong(signal.type);

        //
        // Check if Restrictions Exists ...
        if (!HasRestrictions())
        {
            return result;
        }

        //
        // Check Signal Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedSignalsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLongSignals < mAllowedSignalsInRestrictions;
            }
        }

        //
        // Check Long Signal Restrictions ...
        if (result)
        {
            //
            if (isLong &&
                IsValidSize(mAllowedLongSignalsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLongSignals < mAllowedLongSignalsInRestrictions;
            }
        }

        //
        // Check Short Signal Restrictions ...
        if (result)
        {
            //
            if (!isLong &&
                IsValidSize(mAllowedShortSignalsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedShortSignals < mAllowedShortSignalsInRestrictions;
            }
        }

        //
        return result;
    }

    /**
     * Check Allow Issue Signal or not ...
     *
     * @param  signal: XSignal ...
     *
     * @return ( bool )
     */
    int CheckAllowExecuteSignal(XSignal &signal)
    {
        //
        bool result = false;

        //
        // Validate Signal ...
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }
        bool isLong = IsLong(signal.type);

        //
        // Check if Restrictions Exists ...
        if (!HasRestrictions())
        {
            return result;
        }

        //
        // Check Trade Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedTradesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedTrades < mAllowedTradesInRestrictions;
            }
        }

        //
        // Check Long Trade Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedLongTradesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLongTrades < mAllowedLongTradesInRestrictions;
            }
        }

        //
        // Check Short Trade Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedShortTradesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedShortTrades < mAllowedShortTradesInRestrictions;
            }
        }

        //
        // Check Wins Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedWinsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedWins < mAllowedWinsInRestrictions;
            }
        }

        //
        // Check Long Wins Restriction ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedLongWinsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLongWins < mAllowedLongWinsInRestrictions;
            }
        }

        //
        // Check Short Wins Restriction ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedShortWinsInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedShortWins < mAllowedShortWinsInRestrictions;
            }
        }

        //
        // Check Lose Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedLosesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLoses < mAllowedLosesInRestrictions;
            }
        }

        //
        // Check Long Lose Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedLongLosesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedLongLoses < mAllowedLongLosesInRestrictions;
            }
        }

        //
        // Check Short Lose Restrictions ...
        if (result)
        {
            //
            if (IsValidSize(mAllowedShortLosesInRestrictions))
            {
                //
                result =
                    result &&
                    mRestrictedShortLoses < mAllowedShortLosesInRestrictions;
            }
        }

        //
        return result;
    }
    // #endregion

    // #region Event Handlers Registrations ...
    /**
     * Register Restrictions Start Event Handler ...
     *
     * @param  handler: TRestrictionEvent ...
     */
    void AddRestrictionsStartEventHandler(TRestrictionEvent handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnRestrictionsStartEventHandlers);
    }

    /**
     * Register Restrictions End Event Handler ...
     *
     * @param  handler: TRestrictionEvent ...
     */
    void AddRestrictionsEndEventHandler(TRestrictionEvent handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(handler, mOnRestrictionsEndEventHandlers);
    }
    // #endregion

    /**
     * Process Ticks for Restrictions ...
     * Applied Eac Secconds ...
     */
    void Process()
    {
        //
        // Check Restrictions Exists or not ...
        if (!HasRestrictions())
        {
            return;
        }

        //
        // Calculate Requirements ...
        datetime currentTime = TimeCurrent();

        //
        // Check Current Restrictions Exists or not ...
        // Initial Restrictions Period if not Exists ...
        if (!IsXValid(mRestrictionsStart))
        {
            //
            // Initial Restrictions ...
            mRestrictionsStart = currentTime;
            mRestrictionsEnd = RestrictionsEndTime(
                mRestrictionsPeriod,
                mRestrictionsPeriodMultiplier,
                mRestrictionsStart //
            );

            //
            // Loging Restrictions Start ...
            LogRestrictionsStart();

            //
            // Notfy Restrictions Start Event Handlers ...
            NotifyRestrictionsStartEventHandlers();

            //
            return;
        }

        //
        // Validate Current Restrictions ...

        //
        // Check Process Exists or not ...
        if (IsXValid(mLastCheck))
        {
            //
            // Validate New Check Must Happens after 1 Minute ...
            bool canCheck = currentTime >= mLastCheck + 60;
            if (!canCheck)
            {
                return;
            }
        }

        //
        // Update Last Check ...
        mLastCheck = currentTime;

        //
        // Check Restrictions End ...
        bool isEnded = currentTime >= mRestrictionsEnd;
        if (isEnded)
        {
            //
            // Fire Restrictions End Event Handlers ...
            NotifyRestrictionsEndEventHandlers();

            //
            // Logging Resrtrictions Summary if Required ...
            LogRestrictionsEnd();

            //
            // Reset Restrictions ...
            ResetRestrictions();

            //
            return;
        }

        //
        // TODO: Implement What we want if necessary ...
    }

    //
    // Protected ...
  protected:
    //
    // Props ...

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        mAllowedWinsInRestrictions = 0;
        mAllowedLongWinsInRestrictions = 0;
        mAllowedShortWinsInRestrictions = 0;

        //
        mAllowedLosesInRestrictions = 0;
        mAllowedLongLosesInRestrictions = 0;
        mAllowedShortLosesInRestrictions = 0;

        //
        mAllowedSignalsInRestrictions = 0;
        mAllowedLongSignalsInRestrictions = 0;
        mAllowedShortSignalsInRestrictions = 0;

        //
        mAllowedTradesInRestrictions = 0;
        mAllowedLongTradesInRestrictions = 0;
        mAllowedShortTradesInRestrictions = 0;

        //
        ResetRestrictions();
        XClean(mOnRestrictionsEndEventHandlers);
        XClean(mOnRestrictionsStartEventHandlers);
    }

    /**
     * Set Default Values ...
     */
    void Default()
    {
        //
        mRestrictionsPeriodMultiplier = 1;
        mRestrictionsPeriod = X_TRADE_RESTRICATION_NONE;
    }

    /**
     * Logging Restrictions Start Summary ...
     */
    void LogRestrictionsStart()
    {
    }

    /**
     * Logging Restrictions End Summary ...
     */
    void LogRestrictionsEnd()
    {
    }

    // #region Event Handlers Notifier ...
    /**
     * Notify Registered Restrictions Start Event Handlers ...
     */
    void NotifyRestrictionsStartEventHandlers()
    {
        //
        int count = ArraySize(mOnRestrictionsStartEventHandlers);
        if (!IsValidSize(count))
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            mOnRestrictionsStartEventHandlers[i]();
        }
    }

    /**
     * Notify Registered Restrictions End Event Handlers ...
     */
    void NotifyRestrictionsEndEventHandlers()
    {
        //
        int count = ArraySize(mOnRestrictionsEndEventHandlers);
        if (!IsValidSize(count))
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            mOnRestrictionsEndEventHandlers[i]();
        }
    }
    // #endregion

    // #region Restrictions Summary Cleaners ...
    /**
     * Reset all Restricted Counters ...
     */
    void ResetRestrictions()
    {
        //
        mRestrictionsEnd = NULL;
        mRestrictionsStart = NULL;

        //
        ResetTradeRestrictions();
        ResetSignalRestrictions();
        ResetTradeWinRestrictions();
        ResetTradeLoseRestrictions();
    }

    /**
     * Resetting Signal Restrictions ...
     */
    void ResetSignalRestrictions()
    {
        //
        mRestrictedSignals = 0;
        mRestrictedLongSignals = 0;
        mRestrictedShortSignals = 0;
    }

    /**
     * Resetting Trade Restrictions ...
     */
    void ResetTradeRestrictions()
    {
        //
        mRestrictedTrades = 0;
        mRestrictedLongTrades = 0;
        mRestrictedShortTrades = 0;
    }

    /**
     * Resetting Trade Win Restrictions ...
     */
    void ResetTradeWinRestrictions()
    {
        mWins = 0;
        mLongWins = 0;
        mShortWins = 0;
    }

    /**
     * Resetting Trade Lose Restrictions ...
     */
    void ResetTradeLoseRestrictions()
    {
        //
        mLoses = 0;
        mLongLoses = 0;
        mShortLoses = 0;
    }
    // #endregion ...

    //
    // Private ...
  private:
    //
    // Props ...

    //
    // Last Restrictions Time ...
    datetime mLastCheck;         // Last Restrictions Check ...
    datetime mRestrictionsEnd;   // Last Restrictions End Time ...
    datetime mRestrictionsStart; // Last Restrictions Start Time ...

    //
    // Restrictions Period ...
    int mRestrictionsPeriodMultiplier;                    // Restrictions Period Multiplier ...
    ENUM_X_TRADE_RESTRICTIONS_PERIOD mRestrictionsPeriod; // Restrictions Period ...

    // #region Signals ...
    //
    int mSignals;      // Issued Signals ...
    int mLongSignals;  // Issued Long Signals ...
    int mShortSignals; // Issued Short Signals ...

    //
    int mRestrictedSignals;      // Issued Signals in Restrictions Period ...
    int mRestrictedLongSignals;  // Issued Long Signals in Restrictions Period ...
    int mRestrictedShortSignals; // Issued Short Signals in Restrictions Period ...

    //
    int mAllowedSignalsInRestrictions;      // Allowed Signals in Restrictions Period ...
    int mAllowedLongSignalsInRestrictions;  // Allowed Long Signals in Restrictions Period ...
    int mAllowedShortSignalsInRestrictions; // Allowed Short Signals in Restrictions Period ...
    // #endregion

    // #region Trades ...
    //
    int mTrades;      // Executed Trades ...
    int mLongTrades;  // Executed Long Trades ...
    int mShortTrades; // Executed Short Trades ...

    //
    int mRestrictedTrades;      // Executed Trades in Restrictions Period ...
    int mRestrictedLongTrades;  // Executed Long Trades in Restrictions Period ...
    int mRestrictedShortTrades; // Executed Short Trades in Restrictions Period ...

    //
    int mAllowedTradesInRestrictions;      // Allowed Trades in Restrictions Period ...
    int mAllowedLongTradesInRestrictions;  // Allowed Long Trades in Restrictions Period ...
    int mAllowedShortTradesInRestrictions; // Allowed Short Trades in Restrictions Period ...
    // #endregion

    // #region Wins ...
    //
    int mWins;      // Touched Wins ...
    int mLongWins;  // Touched Long Wins ...
    int mShortWins; // Touched Short Wins ...

    //
    int mRestrictedWins;      // Touched Wins in Restrictions Period ...
    int mRestrictedLongWins;  // Touched Long Wins in Restrictions Period ...
    int mRestrictedShortWins; // Touched Short Wins in Restrictions Period ...

    //
    int mAllowedWinsInRestrictions;      // Allowed Wins in Restrictions Period ...
    int mAllowedLongWinsInRestrictions;  // Allowed Long Wins in Restrictions Period ...
    int mAllowedShortWinsInRestrictions; // Allowed Short Wins in Restrictions Period ...
    // #endregion

    // #region Losess ...
    //
    int mLoses;      // Touched Loses ...
    int mLongLoses;  // Touched Long Loses ...
    int mShortLoses; // Touched Short Loses ...

    //
    int mRestrictedLoses;      // Touched Loses in Restrictions Period ...
    int mRestrictedLongLoses;  // Touched Long Loses in Restrictions Period ...
    int mRestrictedShortLoses; // Touched Short Loses in Restrictions Period ...

    //
    int mAllowedLosesInRestrictions;      // Allowed Loses in Restrictions Period ...
    int mAllowedLongLosesInRestrictions;  // Allowed Long Loses in Restrictions Period ...
    int mAllowedShortLosesInRestrictions; // Allowed Short Loses in Restrictions Period ...
    // #endregion

    // #region Event Handlers ...
    TRestrictionEvent mOnRestrictionsEndEventHandlers[];   // Restrictions End Event Handlers ...
    TRestrictionEvent mOnRestrictionsStartEventHandlers[]; // Restrictions Start Event Hnadlers ...
    // #endregion
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-rm.panel.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCRMPanel
// Description: provides all based classes for use ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-app-dialog.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"

//
#include <ChartObjects/ChartObjectsArrows.mqh>
#include <ChartObjects/ChartObjectsFibo.mqh>
#include <ChartObjects/ChartObjectsLines.mqh>
#include <ChartObjects/ChartObjectsShapes.mqh>
#include <Controls/Button.mqh>
#include <Controls/Edit.mqh>

//
// Implementation ...

//
class XCRMPanel : public XCAppDialog
{
    //
  public:
    //
    // Event Binding ...
    // EVENT_MAP_BEGIN(XCRMPanel)
    // ON_EVENT(ON_CLICK, btnBuy, HandleBtnBuyClicked)
    // ON_EVENT(ON_CLICK, btnSell, HandleBtnSellClicked)
    // EVENT_MAP_END(XCRMPanel)

    //
    // Constructor(s) ...
    XCRMPanel()
    {
        //
        Default();
    }

    //
    // Deconstructor ...
    ~XCRMPanel()
    {
        //
        HandleRemoveObjects();

        //
        ZeroMemory(mTrader);
    }

    //
    // Props ...

    //
    int SubWindow()
    {
        return subWindow;
    }

    //
    void SubWindow(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        subWindow = value;
    }

    //
    // BUY ...

    //
    string BtnBuyLabel()
    {
        return buyLabelStr;
    }

    //
    void BtnBuyLabel(string value)
    {
        buyLabelStr = value;
    }

    //
    color BtnBuyBGColor()
    {
        return btnBuyBGColor;
    }

    //
    void BtnBuyBGColor(color value)
    {
        btnBuyBGColor = value;
    }

    //
    // SELL ...

    //
    string BtnSellLabel()
    {
        return sellLabelStr;
    }

    //
    void BtnSellLabel(string value)
    {
        sellLabelStr = value;
    }

    //
    color BtnSellBGColor()
    {
        return btnSellBGColor;
    }

    //
    void BtnSellBGColor(color value)
    {
        btnSellBGColor = value;
    }

    //
    double RiskPercent()
    {
        return riskPercent;
    }

    //
    void RiskPercent(double value)
    {
        //
        if (value < minAllowedRiskPercent)
        {
            value = minAllowedRiskPercent;
        }

        //
        if (value > maxAllowedRiskPercent)
        {
            value = maxAllowedRiskPercent;
        }

        //
        riskPercent = value;

        //
        tbRisk.Text((string)value);
    }

    //
    //
    //

    bool HasSL()
    {
        return slLine != NULL;
    }

    double ReadSL()
    {
        //
        double result = 0;

        //
        if (HasSL())
        {
            //
            result = slLine.GetDouble(OBJPROP_PRICE);
            result = NormalizePrice(result, _Symbol);
        }

        //
        return result;
    }

    bool HasTP()
    {
        return tpLine != NULL;
    }

    double ReadTP()
    {
        //
        double result = 0;

        //
        if (HasTP())
        {
            //
            result = tpLine.GetDouble(OBJPROP_PRICE);
            result = NormalizePrice(result, _Symbol);
        }

        //
        return result;
    }

    bool HasEntry()
    {
        return entryLine != NULL;
    }

    double ReadEntry()
    {
        //
        double result = 0;

        //
        if (HasEntry())
        {
            //
            result = entryLine.GetDouble(OBJPROP_PRICE);
            result = NormalizePrice(result, _Symbol);
        }

        //
        return result;
    }

    bool GetDirection(ENUM_X_DIRECTION &dir)
    {
        //
        bool result = false;

        //
        dir = X_DIRECTION_NONE;

        //
        result = HasTP() &&
                 HasSL() &&
                 HasEntry();

        //
        if (!result)
        {
            return result;
        }

        //
        double sl = ReadSL();
        double tp = ReadTP();
        double entry = ReadEntry();

        //
        result = sl > 0 &&
                 tp > 0 &&
                 entry > 0;
        if (!result)
        {
            return result;
        }

        //
        bool isBullish =
            tp > entry &&
            tp > sl &&
            entry > sl;
        bool isBearish =
            tp < entry &&
            tp < sl &&
            entry < sl;
        result = isBullish ||
                 isBearish;
        if (!result)
        {
            return result;
        }

        //
        dir = isBullish
                  ? X_DIRECTION_BULLISH
                  : X_DIRECTION_BEARISH;

        //
        return result;
    }

    //
    // Tools ...

    //
    void Default()
    {
        //
        string prefix = CreateInstanceId();

        //
        defaultR2R = 2;
        riskPercent = 1;
        defaultRisk = 50;
        minAllowedRiskPercent = 0.5;
        maxAllowedRiskPercent = 10.0;

        //
        disabledBtnColor = clrLightGray;
        disabledBtnBGColor = clrGray;

        //
        tbRiskID = prefix + "_RiskTB";

        //
        buyLabelStr = "Buy";
        btnBuyBGColor = clrLime;
        btnBuyColor = clrDarkBlue;
        btnBuyID = prefix + "_" + buyLabelStr;

        //
        sellLabelStr = "Sell";
        btnSellBGColor = clrRed;
        btnSellColor = clrDarkBlue;
        btnSellID = prefix + "_" + sellLabelStr;

        //
        slLine = NULL;
        slColor = clrMagenta;
        slID = prefix + "_SL";

        //
        tpLine = NULL;
        tpColor = clrAqua;
        tpID = prefix + "_TP";

        //
        entryLine = NULL;
        entryColor = clrYellow;
        entryID = prefix + "_Entry";

        //
        btnDrawRemoveID = prefix + "_DrawRemove";
        drawLabelStr = "Draw";
        removeLabelStr = "Remove";
        btnDrawColor = clrWhite;
        btnDrawBGColor = clrDarkBlue;
        btnRemoveColor = clrYellow;
        btnRemoveBGColor = clrDarkRed;

        //
        slippage = 10;
        magicNumber = 1694056;

        //
        // Create Trader Instance and Configure it ...
        mTrader = new XCTrade(
            slippage,
            magicNumber //
        );
    }

    //
    void UpdateState()
    {
        //
        bool canEnable = HasSL() &&
                         HasTP() &&
                         HasEntry();

        //
        if (canEnable)
        {
            SetRemoveMode();
        }
        else
        {
            SetDrawMode();
        }

        //
        double entry = GetBid(_Symbol);
        ENUM_X_DIRECTION dir;
        if (GetDirection(dir))
        {
            entry = GetEntry(_Symbol, dir);
        }

        //
        if (HasEntry())
        {
            double iEntry = ReadEntry();
            if (iEntry != entry)
            {
                SetEntry(entry);
            }
        }
    }

    //
    // Handlers ...

    /**
     * Handle Btn Buy Clicked ...
     */
    void HandleBtnBuyClicked()
    {
        //
        ENUM_X_DIRECTION dir;
        bool has = GetDirection(dir);
        bool isBullish = has &
                         IsBullish(dir);
        if (!has || !isBullish)
        {
            return;
        }

        //
        double tp = ReadTP();
        double sl = ReadSL();
        double entry = ReadEntry();

        //
        mTrader.Buy(
            _Symbol,
            _Period,
            volume,
            entry,
            sl,
            tp //
        );
    }

    /**
     * Handle Btn Sell Clicked ...
     */
    void HandleBtnSellClicked()
    {
        //
        ENUM_X_DIRECTION dir;
        bool has = GetDirection(dir);
        bool isBearish = has &
                         IsBearish(dir);
        if (!has || !isBearish)
        {
            return;
        }

        //
        double tp = ReadTP();
        double sl = ReadSL();
        double entry = ReadEntry();

        //
        mTrader.Sell(
            _Symbol,
            _Period,
            volume,
            entry,
            sl,
            tp //
        );
    }

    /**
     * Handle Draw Remove Position ...
     */
    void HandleBtnDrawRemoveClicked()
    {
        //
        string btnText = btnDrawRemove.Text();
        if (btnText == drawLabelStr)
        {
            HandleDrawObjects();
        }
        else if (btnText == removeLabelStr)
        {
            HandleRemoveObjects();
        }
    }

    /**
     * Handle Risk Percent Changed ...
     */
    void HandleOnRiskPercentChanged()
    {
        //
        string tbRiskStr = tbRisk.Text();

        //
        double tbNewRisk = (double)tbRiskStr;
        RiskPercent(tbNewRisk);
    }

    //
    // Virtuals ...

    //
    virtual bool OnEvent(
        const int id,
        const long &lparam,
        const double &dparam,
        const string &sparam //
    )
    {
        //
        bool result = false;

        //
        XCAppDialog::OnEvent(id, lparam, dparam, sparam);

        //
        // Check if the event is a button click
        if (id == ON_CLICK + CHARTEVENT_CUSTOM)
        {
            //
            // Verify if the clicked object is this button
            if (lparam == btnBuy.Id())
            {
                //
                HandleBtnBuyClicked();
                result = true;
            }
            else if (lparam == btnSell.Id())
            {
                //
                HandleBtnSellClicked();
                result = true;
            }
            else if (lparam == btnDrawRemove.Id())
            {
                //
                HandleBtnDrawRemoveClicked();
                result = true;
            }
        }
        else if (id == ON_END_EDIT + CHARTEVENT_CUSTOM)
        {
            //
            if (lparam == tbRisk.Id())
            {
                //
                HandleOnRiskPercentChanged();
                result = true;
            }
        }
        else if (id == CHARTEVENT_OBJECT_DRAG)
        {
            //
            if (sparam == entryLine.Name())
            {
                //
                CalculateData();
                result = true;
            }
            else if (sparam == tpLine.Name())
            {
                //
                CalculateData();
                result = true;
            }
            else if (sparam == slLine.Name())
            {
                //
                CalculateData();
                result = true;
            }
        }

        //
        return result;
    }

    //
    // Overrides ...

    //
    bool Create(
        const long chart,
        const string name,
        const int subwin,
        const int x1,
        const int y1,
        const int x2,
        const int y2 //
    )
    {
        //
        bool result = false;

        //
        // Create App Dialog instance it Self ...
        result = XCAppDialog::Create(
            chart,
            name,
            subwin,
            x1,
            y1,
            x2,
            y2 //
        );
        if (!result)
        {
            return result;
        }

        //
        chartId = chart;
        subWindow = subwin;

        //
        // Configure Dialog ...
        BackgroundColor(clrBlack);

        //
        int top = Top();
        int left = Left();
        int right = Right();
        int bottom = Bottom();

        //
        int width = Width();
        int height = Height();

        //
        int desiredWidth = width - left;
        int desiredHeight = height - top;

        //
        int btnWidth = desiredWidth / 2;
        int btnHeight = desiredHeight / 3;

        //
        // Risk Edit ...
        int tbRiskX1 = 0;
        int tbRiskY1 = 0;
        int tbRiskX2 = tbRiskX1 + (btnWidth * 2);
        int tbRiskY2 = tbRiskY1 + btnHeight;
        result = tbRisk.Create(
            chart,
            tbRiskID,
            subWindow,
            tbRiskX1,
            tbRiskY1,
            tbRiskX2,
            tbRiskY2 //
        );
        if (!result)
        {
            return result;
        }

        //
        result = tbRisk.Text((string)riskPercent);
        result = tbRisk.TextAlign(ALIGN_CENTER);

        //
        result = Add(tbRisk);

        //
        // Create Draw/Remove Button ...
        int btnDrawX1 = 0;
        int btnDrawY1 = desiredHeight - (btnHeight * 2);
        int btnDrawX2 = btnDrawX1 + (btnWidth * 2);
        int btnDrawY2 = btnDrawY1 + btnHeight;
        result = btnDrawRemove.Create(
            chart,
            btnDrawRemoveID,
            subWindow,
            btnDrawX1,
            btnDrawY1,
            btnDrawX2,
            btnDrawY2 //
        );
        if (!result)
        {
            return result;
        }

        //
        result = btnDrawRemove.Text(drawLabelStr);
        result = btnDrawRemove.Color(btnDrawColor);
        result = btnDrawRemove.ColorBackground(btnDrawBGColor);

        //
        result = Add(btnDrawRemove);

        //
        // Create Buy Button ...
        int btnBuyX1 = 0;
        int btnBuyY1 = desiredHeight - btnHeight;
        int btnBuyX2 = btnBuyX1 + btnWidth;
        int btnBuyY2 = btnBuyY1 + btnHeight;
        result = btnBuy.Create(
            chart,
            btnBuyID,
            subWindow,
            btnBuyX1,
            btnBuyY1,
            btnBuyX2,
            btnBuyY2 //
        );
        if (!result)
        {
            return result;
        }

        //
        result = btnBuy.Text(buyLabelStr);
        result = btnBuy.Color(btnBuyColor);
        result = btnBuy.ColorBackground(btnBuyBGColor);

        //
        result = Add(btnBuy);

        //
        // Create Sell Button ...
        int btnSellX1 = btnBuyX2;
        int btnSellY1 = btnBuyY1;
        int btnSellX2 = btnSellX1 + btnWidth;
        int btnSellY2 = btnSellY1 + btnHeight;
        result = btnSell.Create(
            chart,
            btnSellID,
            subWindow,
            btnSellX1,
            btnSellY1,
            btnSellX2,
            btnSellY2 //
        );
        if (!result)
        {
            return result;
        }

        //
        result = btnSell.Text(sellLabelStr);
        result = btnSell.Color(btnSellColor);
        result = btnSell.ColorBackground(btnSellBGColor);

        //
        result = Add(btnSell);

        //
        if (result)
        {
            //
            UpdateState();
            CalculateData();
        }

        //
        return result;
    }

    //
  protected:
    //
    XCTrade *mTrader;
    int slippage;
    long magicNumber;

    //
    long chartId;
    int subWindow;

    //
    string slID;
    color slColor;
    CChartObjectHLine *slLine;

    //
    string tpID;
    color tpColor;
    CChartObjectHLine *tpLine;

    //
    string entryID;
    color entryColor;
    CChartObjectHLine *entryLine;

    //
    double riskPercent;
    double minAllowedRiskPercent;
    double maxAllowedRiskPercent;

    //
    double volume;

    //
    double defaultR2R;
    double defaultRisk;

    //
    color disabledBtnColor;
    color disabledBtnBGColor;

    //
    // Risk Percent Edit ...
    CEdit tbRisk;
    string tbRiskID;

    //
    // BUY Button ...
    CButton btnBuy;
    string btnBuyID;
    string buyLabelStr;
    color btnBuyColor;
    color btnBuyBGColor;

    //
    // SELL Button ...
    CButton btnSell;
    string btnSellID;
    string sellLabelStr;
    color btnSellColor;
    color btnSellBGColor;

    //
    // DRAW / REMOVE Button ...
    CButton btnDrawRemove;
    string btnDrawRemoveID;
    string drawLabelStr;
    string removeLabelStr;
    color btnDrawColor;
    color btnDrawBGColor;
    color btnRemoveColor;
    color btnRemoveBGColor;

    //
  private:
    //

    bool EnableBuy(bool value)
    {
        //
        bool result = false;

        //
        btnBuy.Locking(!value);
        btnSell.Pressed(value);
        if (!value)
        {
            //
            // Disabled ...
            btnBuy.Color(disabledBtnColor);
            btnBuy.ColorBackground(disabledBtnBGColor);
        }
        else
        {
            //
            // Enable ...
            btnBuy.Color(btnBuyColor);
            btnBuy.ColorBackground(btnBuyBGColor);
        }

        //
        return result;
    }

    bool EnableSell(bool value)
    {
        //
        bool result = false;

        //
        btnSell.Locking(!value);
        btnSell.Pressed(value);
        if (!value)
        {
            //
            // Disabled ...
            btnSell.Color(disabledBtnColor);
            btnSell.ColorBackground(disabledBtnBGColor);
        }
        else
        {
            //
            // Enable ...
            btnSell.Color(btnSellColor);
            btnSell.ColorBackground(btnSellBGColor);
        }

        //
        return result;
    }

    bool SetDrawMode()
    {
        //
        bool result = false;

        //
        result = btnDrawRemove.Text(drawLabelStr);
        result = btnDrawRemove.Color(btnDrawColor);
        result = btnDrawRemove.ColorBackground(btnDrawBGColor);

        //
        return result;
    }

    bool SetRemoveMode()
    {
        //
        bool result = false;

        //
        result = btnDrawRemove.Text(removeLabelStr);
        result = btnDrawRemove.Color(btnRemoveColor);
        result = btnDrawRemove.ColorBackground(btnRemoveBGColor);

        //
        return result;
    }

    void HandleDrawObjects()
    {
        //
        double entry = GetBid(_Symbol);
        double points = GetPoints(_Symbol);
        double riskRatio = defaultRisk * points;
        double rewardRatio = riskRatio * defaultR2R;
        double sl = entry - riskRatio;
        double tp = entry + rewardRatio;

        //
        datetime rectTime1 = iTime(
            _Symbol,
            _Period,
            20 //
        );
        datetime rectTime2 = iTime(
            _Symbol,
            _Period,
            19 //
        );

        //
        bool has = false;

        //
        // Create Entry Line ...
        entryLine = new CChartObjectHLine();
        has = entryLine.Create(
            chartId,
            entryID,
            subWindow,
            entry //
        );
        if (!has)
        {
            //
            HandleRemoveObjects();
            return;
        }
        entryLine.Selectable(false);
        entryLine.Color(entryColor);

        //
        // Create SL Line ...
        slLine = new CChartObjectHLine();
        has = slLine.Create(
            chartId,
            slID,
            subWindow,
            sl //
        );
        if (!has)
        {
            //
            HandleRemoveObjects();
            return;
        }
        slLine.Color(slColor);
        slLine.Selectable(true);

        //
        // Create TP Line ...
        tpLine = new CChartObjectHLine();
        has = tpLine.Create(
            chartId,
            tpID,
            subWindow,
            tp //
        );
        if (!has)
        {
            //
            HandleRemoveObjects();
            return;
        }
        tpLine.Color(tpColor);
        tpLine.Selectable(true);

        //
        CalculateData();
    }

    void HandleRemoveObjects()
    {
        //
        if (HasSL())
        {
            //
            slLine.Delete();
            slLine.Detach();
            ZeroMemory(slLine);
        }

        //
        if (HasTP())
        {
            //
            tpLine.Delete();
            tpLine.Detach();
            ZeroMemory(tpLine);
        }

        //
        if (HasEntry())
        {
            //
            entryLine.Delete();
            entryLine.Detach();
            ZeroMemory(entryLine);
        }

        //
        UpdateState();
        CalculateData();
    }

    void CalculateData()
    {
        //
        EnableBuy(false);
        EnableSell(false);

        //
        ENUM_X_DIRECTION dir;
        bool has = GetDirection(dir);
        if (!has)
        {
            return;
        }

        //
        bool isBullish = IsBullish(dir);
        bool isBearish = IsBearish(dir);

        //
        if (isBullish)
        {
            //
            EnableBuy(true);
            EnableSell(false);
        }
        else if (isBearish)
        {
            //
            EnableSell(true);
            EnableBuy(false);
        }

        //
        double sl = ReadSL();
        double tp = ReadTP();
        double entry = ReadEntry();
        double points = GetPoints(_Symbol);

        //
        double risk = MathAbs(entry - sl);
        int riskPoints = (int)(risk / points);
        double riskRewardRatio = MathAbs(tp - entry) / risk;
        riskRewardRatio = NormalizeDouble(riskRewardRatio, 2);

        //
        double balance = GetBalance();

        //
        double riskAmount = riskPercent * (balance / 100);
        riskAmount = NormalizePrice(riskAmount, _Symbol);

        //
        double rewardAmount = riskAmount * riskRewardRatio;
        rewardAmount = NormalizePrice(rewardAmount, _Symbol);

        //
        string currency = mTrader.mAccount.GetCurrency();

        //
        volume = mTrader
                     .mAccount
                     .CalculateVolume(
                         _Symbol,
                         riskAmount,
                         riskPoints //
                     );
        volume = NormalizeVolume(volume, _Symbol);

        //
        string slDesc = "SL: " + ToXString(sl) +
                        ", Risk: " + ToXString(riskPoints) + " pt (" + ToXString(riskPercent) + "%)" +
                        ", Amount: " + ToXString(riskAmount) + currency;
        slLine.Tooltip(slDesc);
        slLine.Description(slDesc);

        //
        string tpDesc = "TP: " + ToXString(tp) + " (" + ToXString(rewardAmount) + " " + currency + ")" +
                        ", R2R: 1:" + ToXString(riskRewardRatio);
        tpLine.Tooltip(tpDesc);
        tpLine.Description(tpDesc);

        //
        string entryDesc = "Entry: " + ToXString(entry) +
                           ", Volume: " + ToXString(volume);
        entryLine.Tooltip(entryDesc);
        entryLine.Description(entryDesc);

        //
    }

    void SetEntry(double value)
    {
        //
        if (value <= 0 ||
            !HasEntry())
        {
            return;
        }

        //
        bool has = entryLine
                       .SetDouble(OBJPROP_PRICE, value);
    }

    double GetBalance()
    {
        //
        double result = 0;

        //
        // TODO: Change This ...
        return mTrader.mAccount.GetBalance();

        //
        return result;
    }

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-signaller.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class
// -------------------------------------------------
// Name: XCBaseSignaller ...
// Description: Base Signaller Class ...
//
// - Create an Instance based on this ...
// - Attach Required Helpers for Indicator Usages ...
// - Override Virtual Methdos:
//  - [] ProcessTick: Processing All Ticks
//  - [] ProcessNewBar: Processing All New Bars
//  - [] CheckForGuard: Check For Guards
//  - [] NormalizeSignal: Normalizing Prepared Signal ...
//  - [] CheckAdditionslSignallerValidations: Additional Class Validations
//
// - Dont Forget to Call OnSignal Event in Process Methods when
// a signal Appears ...
//
// - also Whenever you want you can call PrepareSignal Action to
// provide a Signal based on Current Market State ...
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm Base Signaller Class"
#property strict

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-market.pattern.class.mq5"
#include "../Libraries/x-saherelm.x-trade.lib.mq5"

//
// Definitions ...

//
// Implementations ...
class XCBaseSignaller : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...
    string name;            // Name ...
    string symbol;          // Symbol ...
    ENUM_TIMEFRAMES period; // Period ...

    //
    bool ignoreTargets;            // Ignore Signal Targets ...
    double slAdditionDistance;     // SL Addition Distance ...
    double maxAllowedRiskDistance; // Max Allowed Risk In Points ...

    //
    // Restrictions ...
    datetime restUntil;    // Resting Time ...
    int restingSignals;    // Resting Signals ...
    datetime lastSignalAt; // Holding Last Signal's Time ...
    int restingDelay;      // Resting Bars After Last Signal ...
    int loosingTrades;     // Loosing Trades ...

    //
    TCanAnalyse canAnalyseEventListener;

    //
    XCMarketPatternDetector *patternDetector;

    //
    // Constructor ...
    XCBaseSignaller(
        string _name,                          //  Name ...
        string _symbol,                        // Symbol ...
        ENUM_TIMEFRAMES _period,               // Period ...
        bool _processAllTicks = false,         // Process All Ticks ...
        double _defaultSignalR2R = 1.5,        // Default Signal Risk To Reward Ratio ...
        double _defaultSignalRiskInPoint = 50, // Default Signal Risk In Point ...
        double _defaultSignalVolume = 0.01     // Default Signal Volume ...
    )
    {
        //
        // Assign Properties ...
        name = _name;
        symbol = _symbol;
        period = _period;

        //
        ignoreTargets = false;
        slAdditionDistance = 0;
        maxAllowedRiskDistance = 0;

        //
        processAllTicks = _processAllTicks;
        defaultSignalR2R = _defaultSignalR2R;
        defaultSignalVolume = _defaultSignalVolume;
        defaultSignalRiskInPoint = _defaultSignalRiskInPoint;

        //
        // Normalize ...
        defaultSignalR2R = NormalizeDouble(defaultSignalR2R, 1, 10);
        defaultSignalVolume = NormalizeDouble(defaultSignalVolume, 0.01);
        defaultSignalVolume = NormalizeVolume(defaultSignalVolume, symbol, 2);
        defaultSignalRiskInPoint = NormalizeDouble(defaultSignalRiskInPoint, 10);

        //
        // Set Dfault Values ...
        mProcessedTicksCount = 0;
        mLastCheckedBarTime = NULL;

        //
        patternDetector = new XCMarketPatternDetector();
    }

    //
    // Deconstructor ...
    ~XCBaseSignaller()
    {
        //
        // Cleanup Event Listeners ...
        SpecifiedClean(onSignalEventListeners);

        //
        delete patternDetector;
        ZeroMemory(patternDetector);
    }

    //
    // Event Listeners ...

    /**
     * Add On Signal Event Listener ...
     *
     * @param  listener: TOnSignal instance ...
     */
    void AddOnSignalEventHandler(TOnSignal listener)
    {
        //
        // Validate ...
        if (listener == NULL)
        {
            return;
        }

        //
        Add(
            listener,
            onSignalEventListeners //
        );
    }

    //
    // Actions ...

    /**
     * Check Signaller Validation ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(name) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period) &&
            //
            patternDetector != NULL &&
            //
            CheckAdditionslSignallerValidations()
            //
            ;

        //
        return result;
    }

    /**
     * Handling On Tick Event ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     */
    void OnTick(int barIndex = 0)
    {
        //
        // Normalize Bar Index ...
        if (barIndex < 0)
        {
            barIndex = 0;
        }

        //
        // Define Requirements ...
        XOHCL iBar;
        bool has = false;
        bool isNewBar = false;

        //
        // Get Current Bar Time ...
        datetime cBarTime = GetBarTime(
            symbol,
            period,
            barIndex //
        );

        //
        // Validate Bar Time ...
        has = IsSpecifiedValid(cBarTime);
        if (!has)
        {
            return;
        }

        //
        // Check Rest Until ...
        has = !IsXValid(restUntil) || (IsXValid(restUntil) && cBarTime > restUntil);
        if (!has)
        {
            return;
        }

        //
        // Check Process Permissions ...
        has = CanAnalyseMarket(cBarTime);
        if (!has)
        {
            return;
        }

        //
        // Check if new Bar, Reset Processed Ticks Count ...
        isNewBar = cBarTime > mLastCheckedBarTime;
        if (isNewBar)
        {
            //
            mProcessedTicksCount = 0;
            ProcessNewBar(barIndex);
            return;
        }

        //
        // Check Allow Process ...
        // Process All Ticks ...
        // Process New Bars ...
        has = processAllTicks ||
              (!processAllTicks && isNewBar);
        if (!has)
        {
            return;
        }

        //
        // Set Last Works ...
        mProcessedTicksCount++;
        mLastCheckedBarTime = cBarTime;

        //
        ProcessTick(barIndex);
    }

    //
    // Virtuals ...

    /**
     * Processing Tick ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     */
    virtual void ProcessTick(int barIndex = 0)
    {
    }

    /**
     * Processing New Bar ...
     *
     * @param  barIndex: int, Specified Bar Index ...
     */
    virtual void ProcessNewBar(int barIndex = 0)
    {
    }

    /**
     * Check Guards Based on MarketConditions ...
     *
     * @param  guards: XGuard, reference collection to holds result ...
     * @param  positions: XPosition, reference collection to provides positions ...
     * @param  barIndex: int
     *
     * @return ( int )
     */
    int virtual CheckForGuard(
        XGuard &guards[],
        XPosition &positions[],
        int barIndex = 0 //
    )
    {
        //
        int result = 0;

        //
        SpecifiedClean(guards);

        //
        result = ArraySize(guards);

        //
        return result;
    }

    /**
     * Check Additional Validations on Childs Instances ...
     *
     * @return ( bool virtual )
     */
    bool virtual CheckAdditionslSignallerValidations()
    {
        return true;
    }

    /**
     * Normalizing Signal by Signaller Custom Props ...
     *
     * @param  signal: XSignal ...
     */
    void virtual NormalizeSignal(XSignal &signal)
    {
    }

    /**
     * Prepare Restrictions Update For Losing Trades ...
     */
    void LoseTrade()
    {
        //
        restingSignals++;
        loosingTrades++;
    }

    /**
     * Prepare Restrictions For Winning Trades ...
     */
    void WinTrade()
    {
        //
        if (loosingTrades > 0)
        {
            loosingTrades--;
        }

        //
        if (restingSignals > 0)
        {
            restingSignals--;
        }
    }

    //
    // Protected ...
  protected:
    //
    // Props ...

    //
    bool processAllTicks; // Specified Processing All Ticks or not ...

    //
    int mProcessedTicksCount;     // Prcoessed Ticks Count ...
    datetime mLastCheckedBarTime; // Last Checked Bar Time ...

    //
    double defaultSignalR2R;         // Default Signal Risk To Reward Ratio ...
    double defaultSignalVolume;      // Default Signal Volume ...
    double defaultSignalRiskInPoint; // Default Signal Risk In Point ...

    //
    // Event Notifiers ...

    /**
     * Notify OnSignal Event Listeners ...
     *
     * @param  signal: XSignal, Specified Signal to Notify ...
     */
    void NotifySignalEventListeners(XSignal &signal)
    {
        //
        int count = ArraySize(onSignalEventListeners);

        //
        // Validate ...
        if (!IsValidSize(count) || !signal.IsValid())
        {
            return;
        }

        //
        // Check Restrictions ...
        if (HasRestrictions())
        {
            //
            bool ignore = HandleRestrictions(signal);
            if (ignore)
            {
                return;
            }
        }

        //
        // Do Notify ...
        for (int i = 0; i < count; i++)
        {
            onSignalEventListeners[i](signal);
        }

        //
        lastSignalAt = signal.time;
    }

    /**
     * Calling Can Analyse Event Listeners ...
     * Check Can Analayse Market or not ...
     *
     * @param  time: datetime, Specified Time for Checking ...
     *
     * @return ( bool )
     */
    bool CanAnalyseMarket(datetime time = NULL)
    {
        //
        bool result = false;

        //
        // Normalize ...
        time = NormalizeTime(time);

        //
        // Validation ...
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = canAnalyseEventListener == NULL;
        if (result)
        {
            return result;
        }

        //
        // Calling Can Analyse Event Listener ...
        result = canAnalyseEventListener(
            symbol,
            period,
            time //
        );

        //
        return result;
    }

    /**
     * Preparing Default Signal ...
     *
     * @param  signal: XSignal, holding result Signal ...
     * @param  forDir: ENUM_X_DIRECTION
     * @param  targets: XTarget Array, Provides Signal Targets (Price) ...
     * @param  r2r: double, Provides Signal (r2r), if not Provided uses Default R2R ...
     * @param  slPrice: double, Provides Signal SL Price, if not Provided uses riskInPoint ...
     * @param  riskInPoint: double, Provides Signal Risk Amount which Multiplied to Points, if not Provided uses DefaultRiskInPoint ...
     *
     * @return ( bool )
     */
    bool PrepareSignal(
        XSignal &signal,
        ENUM_X_DIRECTION forDir,
        XTarget &targets[], //
        double r2r = 0,
        double slPrice = 0,
        double riskInPoint = 0 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        signal.Clean();

        //
        // Normalize ...

        //
        // Validate ...
        result = IsValid() &&
                 HasDirection(forDir);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = IsSpecifiedBullish(forDir);

        //
        double point = GetPoints(symbol);
        double spread = GetSpread(symbol);
        double volume = defaultSignalVolume;
        double entry = GetEntry(symbol, forDir);

        //
        riskInPoint = riskInPoint > 0 && slPrice <= 0
                          ? riskInPoint
                          : defaultSignalRiskInPoint;
        double risk = (riskInPoint * point);
        risk = slPrice > 0
                   ? MathAbs(entry - slPrice)
                   : risk;

        //
        r2r = r2r > 0
                  ? r2r
                  : defaultSignalR2R;
        double reward = (risk * r2r) + spread;

        //
        double tp =
            isBullish
                ? entry + reward
                : entry - reward;

        //
        double sl =
            isBullish
                ? entry - risk
                : entry + risk;

        //
        // Filling Values ...
        signal.tp = tp;
        signal.sl = sl;
        signal.entry = entry;
        signal.provider = name;
        signal.symbol = symbol;
        signal.period = period;
        signal.volume = volume;
        signal.time = TimeCurrent();
        signal.type =
            isBullish
                ? POSITION_TYPE_BUY
                : POSITION_TYPE_SELL;
        signal.mode = X_ORDER_MODE_MARKET;

        //
        // Apply Targets ...
        if (HasChild(targets))
        {
            //
            ApplyTargetsOnSignal(
                forDir,
                targets,
                signal //
            );
        }

        //
        // Validate Signal ...
        result = signal.IsValid();
        if (result)
        {
            //
            NormalizeSignal(signal);
            result = signal.IsValid();
        }

        //
        if (!result)
        {
            signal.Clean();
        }

        //
        return result;
    }

    /**
     * Check Signaller Has Restrictions or not ...
     *
     * @return ( bool )
     */
    bool HasRestrictions()
    {
        //
        bool result = false;

        //
        result =
            //
            // Rest Delay ...
            restingDelay > 0 ||
            //
            // Resting Signals ...
            restingSignals > 0 ||
            //
            // Rest Until ...
            IsXValid(restUntil)
            //
            ;

        //
        return result;
    }

    /**
     * Handle Restrictions On Signal ...
     *
     * @param  signal: XSignal, reference to Provided Sginal ...
     *
     * @return ( bool )
     */
    bool HandleRestrictions(XSignal &signal)
    {
        //
        // Ignore Signal Execution ...
        bool result = true;

        //
        // Check Restrictions Exists ...
        if (!HasRestrictions())
        {
            //
            // Disable Signal Ignoring and Allow Notify Signal ...
            result = false;
            return result;
        }

        //
        datetime cTime = TimeCurrent();

        //
        // Resting Signals ...
        if (restingSignals > 0)
        {
            //
            restingSignals--;
            return result;
        }

        //
        // Rest Until ...
        if (IsXValid(restUntil))
        {
            //
            if (cTime > restUntil)
            {
                //
                restUntil = NULL;

                //
                // Disable Signal Ignoring and Allow Notify Signal ...
                result = false;
                return result;
            }
            else
            {
                //
                // Ignore Signal ...
                return result;
            }
        }

        //
        // Resting Delay ...
        if (restingDelay > 0 &&
            IsXValid(lastSignalAt))
        {
            //
            int lastSignalIDX = GetBarIndex(
                signal.symbol,
                signal.period,
                lastSignalAt //
            );
            if (IsValidIndex(lastSignalIDX))
            {
                //
                if (lastSignalIDX > restingDelay)
                {
                    //
                    restingDelay = 0;
                    //
                    // Disable Signal Ignoring and Allow Notify Signal ...
                    result = false;
                    return result;
                }
                else
                {
                    //
                    // Ignore Signal ...
                    return result;
                }
            }
        }

        //
        return result;
    }

    /**
     * Reset All Restrictions ...
     */
    void ResetRestrictions()
    {
        //
        restingDelay = 0;
        restUntil = NULL;
        restingSignals = 0;
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    // Event Listeners ...
    TOnSignal onSignalEventListeners[];
};

//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-target.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XCTarget
// Description: provides all Positions Target requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"

//
// Implementation ...
class XCTarget : public XCBase
{
    //
    // Public ...
  public:
    //
    XCTrade *trader;
    XCAlert *alert;
    XSignal signals[];

    //
    // Constructor ...
    XCTarget(
        XCTrade *_trader, //
        XCAlert *_alert   //
        ) : XCBase()
    {
        //
        alert = _alert;
        trader = _trader;

        //
        XClean(signals);
    }

    //
    // Deconstructor ...
    ~XCTarget()
    {
        Destroy();
    }

    //
    void Destroy()
    {
        //
        XClean(signals);
    }

    //
    // Actions ...

    /**
     * Manage Positions ...
     *
     * @param  barIndex: int, Bar Index ...
     */
    void Manage(int barIndex = 0)
    {
        //
        // Normalize ...
        barIndex = NormalizeInt(barIndex, 0);

        //
        // Extract In Profit Positions ...
        XPosition positions[];
        int count = GetInProfitPositions(positions);
        bool has = IsValidIndex(count);
        if (!has)
        {
            return;
        }

        //
        XSignal iSignal;
        XTarget iTarget;
        int signalIDX = -1;
        int targetIDX = -1;
        XPosition iPosition;

        //
        // Loop through Positions ...
        for (int i = 0; i < count; i++)
        {
            //
            // Select Positions ...
            iPosition = positions[i];
            has = FindSignal(
                signalIDX,
                iPosition.ticket //
            );
            if (!has)
            {
                continue;
            }
            iSignal = signals[signalIDX];

            //
            // Selecting Target ...
            targetIDX = iSignal.appliedTargetIDX;
            if (iSignal.isTargetApplied)
            {
                targetIDX++;
            }

            //
            // Validate Targets Size ...
            has = HasChild(iSignal.targets) &&
                  targetIDX < ArraySize(iSignal.targets);
            if (has)
            {
                //
                // Select Target ...
                iTarget = iSignal.targets[targetIDX];

                //
                // Check Can Apply Target and Apply it ...
                has = HandleApplyTarget(iTarget, iPosition);
                if (has)
                {
                    //
                    // Update Target IDX ...
                    iSignal.appliedTargetIDX++;
                }
            }

            //
            // Update Signal ...
            signals[signalIDX] = iSignal;

            //
            // Cleanup ...
            iSignal.Clean();
            iTarget.Clean();
            iPosition.Clean();
        }

        //
        // Cleanup Resources ...
        iSignal.Clean();
        iTarget.Clean();
        iPosition.Clean();
    }

    /**
     * Extract in Profit Positions ...
     *
     * @param  positions: XPosition, reference collection to holds result ...
     *
     * @return ( int )
     */
    int GetInProfitPositions(
        XPosition &positions[] //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(positions);

        //
        // Validate ...
        if (trader == NULL)
        {
            return result;
        }

        //
        result = trader.GetInProfitPositions(
            positions,
            NULL,                  // All Symbols ...
            NULL,                  // All Providers ...
            NULL,                  // All Periods ...
            NULL,                  // All Position Types ...
            X_POSITION_SELECT_ALL, // Select Method ...
            true,                  // Filter by Magic ...
            true                   // Force Clean ...
        );
        if (IsValidSize(result))
        {
            //
            XPosition iPos;
            XPosition tmp[];
            Copy(
                positions,
                tmp //
            );
            XClean(positions);
            while (HasChild(tmp))
            {
                //
                iPos = tmp[0];
                ArrayRemove(
                    tmp,
                    0,
                    1 //
                );

                //
                int idx = -1;
                bool has = FindSignal(
                    idx,
                    iPos.ticket //
                );
                if (has)
                {
                    //
                    AddRef(
                        iPos,
                        positions //
                    );
                }
            }

            //
            XClean(tmp);
            iPos.Clean();

            //
            result = ArraySize(positions);
        }

        //
        return result;
    }

    /**
     * Add Executed Signal ...
     *
     * @param  signal: XSignal, reference to Executed Signal ...
     */
    void AddExecutedSignal(XSignal &signal)
    {
        //
        // Validate ...
        if (!signal.IsValid() || !signal.IsExecuted())
        {
            return;
        }

        //
        AddRef(
            signal,
            signals //
        );
    }

    //
    // Trade Event Handlers ...

    /**
     * Handle Stop Loss Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleOnStopLossTriggered(const XDeal &deal)
    {
        RemoveSignal(deal.positionId);
    }

    /**
     * Handle Take Profit Event Triggered ...
     *
     * @param deal: XDeal instance refrence, provides Triggered Deal info ...
     */
    void HandleOnTakeProfitTriggered(const XDeal &deal)
    {
        RemoveSignal(deal.positionId);
    }

    /**
     * Handle Position Force Closed Event ...
     *
     * @param ticket: ulong, triggered Positions ticket ...
     * @param position: XPosition instance refrence, Triggered Position ...
     * @param comment: string, Comment ...
     */
    void HandleOnPositionForceClosed(
        const ulong ticket,
        const XPosition &position,
        const string comment //
    )
    {
        RemoveSignal(ticket);
    }

    //
    // Event Handlers Registration ...

    //
    void AddOnStopLossEventHandler(TOnStopLoss handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mStopLossEventHandlers
            //
        );
    }

    //
    void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTakeProfitEventHandlers
            //
        );
    }

    //
    void AddOnForceClosePositionEventHandler(TOnForceClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnForceCloseEventHandlers
            //
        );
    }

    //
    // Protected ...
  protected:
    //

    /**
     * Find Specified Signal ...
     *
     * @param  index: int, reference to holds detected index ...
     * @param  signal: XSignal, reference to Specified Signal ...
     *
     * @return ( bool )
     */
    bool FindSignal(
        int &index,
        XSignal &signal //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        index = -1;

        //
        // Validate ...
        result = signal.IsValid() &&
                 HasChild(signals);
        if (!result)
        {
            return result;
        }

        //
        // Looping Through Signals ...
        int count = ArraySize(signals);
        for (int i = 0; i < count; i++)
        {
            //
            result = signals[i]
                         .IsSameAs(signal);
            if (result)
            {
                //
                index = i;
                break;
            }
        }

        //
        result = IsValidIndex(index);

        //
        return result;
    }

    /**
     * Find Specified Signal ...
     *
     * @param  index: int, reference to holds detected index ...
     * @param  ticket: ulong, Specified Signal Position ID ...
     *
     * @return ( bool )
     */
    bool FindSignal(
        int &index,
        ulong ticket //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        index = -1;

        //
        // Validate ...
        result = NotEmptyZero(ticket) &&
                 HasChild(signals);
        if (!result)
        {
            return result;
        }

        //
        // Looping Through Signals ...
        int count = ArraySize(signals);
        for (int i = 0; i < count; i++)
        {
            //
            result = signals[i].IsValid() &&
                     signals[i].IsExecuted() &&
                     signals[i].positionId == ticket;
            if (result)
            {
                //
                index = i;
                break;
            }
        }

        //
        result = IsValidIndex(index);

        //
        return result;
    }

    /**
     * Remove Specified Signal ...
     *
     * @param  ticket: ulong, Specified Ticket ...
     */
    void RemoveSignal(ulong ticket)
    {
        //
        bool has = NotEmptyZero(ticket) &&
                   HasChild(signals);
        if (!has)
        {
            return;
        }

        //
        int idx = -1;
        has = FindSignal(
            idx,
            ticket //
        );
        if (!has)
        {
            return;
        }

        //
        ArrayRemove(
            signals,
            idx,
            1 //
        );
    }

    /**
     * Check Can Apply Specified Target on Specified Position or not ...
     *
     * @param  target: XTarget, reference to Specified Target for Apply ...
     * @param  position: XPosition, reference to Specified Position ...
     *
     * @return ( bool )
     */
    bool CanApplyTarget(
        XTarget &target,
        XPosition &position //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result =
            target.IsValid() &&
            position.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Check Price Reached Targets Value and Distance ...
        double targetDistance = (target.actingDistance > 0 ? target.actingDistance : 0) * position.GetPointsValue();
        result =
            IsLong(position.type)
                ? position.price >= target.target + targetDistance
                : position.price <= target.target - targetDistance;

        //
        return result;
    }

    /**
     * Apply Specified Target on Specified Position ...
     *
     * @param  target: XTarget, reference to Specified Target for Apply ...
     * @param  position: XPosition, reference to Specified Position ...
     *
     * @return ( bool )
     */
    bool HandleApplyTarget(
        XTarget &target,
        XPosition &position //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result =
            target.IsValid() &&
            position.IsValid() &&
            CanApplyTarget(
                target,
                position //
            );
        if (!result)
        {
            return result;
        }

        //
        // Apply Target ...
        string comment = NULL;
        string message = NULL;

        //
        // Do Risk Free if Provided ...
        if (result &&
            target.doRF)
        {
            //
            comment = "Taregt Manager do RF ...";
            result = trader.Modify(
                position.ticket,
                target.target, // SL ...
                position.tp,   // TP ...
                comment        //
            );

            //
            // Handle Alert ...
            if (result)
            {
                //
                message = "Target Manager do RF " + ToXString(position.type) + " Position (" + ToXString(position.ticket) + ") on: " + ToXString(position.price);
                alert.SendAlert(message);
            }
        }

        //
        // Do Risk Free on Entry if Provided ...
        if (result &&
            target.doRFOnEntry)
        {
            //
            comment = "Target Manager do RF on Entry ...";
            double actingDistance = position.GetPointsValue() * target.actingDistance;
            bool isLong = IsLong(position.type);
            double sl = position.entry + (isLong
                                              ? actingDistance
                                              : (-1 * actingDistance) //
                                         );
            result = trader.Modify(
                position.ticket,
                sl,          // SL ...
                position.tp, // TP ...
                comment      //
            );

            //
            // Handle Alert ...
            if (result)
            {
                //
                message = "Target Manager do RF " + ToXString(position.type) + " Position (" + ToXString(position.ticket) + ") on Entry ...";
                alert.SendAlert(message);
            }
        }

        //
        // Do Partial Close if Provided ...
        if (result &&
            target.volumeMultiplier > 0)
        {
            //
            double volume = NormalizeVolume((position.volume * target.volumeMultiplier), position.symbol);
            if (volume != position.volume)
            {
                //
                comment = "Target Manager do Partial Close ...";
                result = trader.ClosePartial(
                    position.ticket,
                    volume,
                    comment //
                );

                //
                // Handle Alert ...
                if (result)
                {
                    //
                    message = "Target Manager do Partial Close " + ToXString(position.type) + " Position (" + ToXString(position.ticket) + ") by: " + ToXString(volume);
                    alert.SendAlert(message);
                }
            }
        }

        //
        // Handle Replace TP ...
        if (result &&
            target.tpValue > 0)
        {
            //
            // Validate TP Value ...
            bool isLong = IsLong(position.type);
            bool isValid = isLong
                               ? target.tpValue > position.price &&
                                     target.tpValue > position.tp
                               : target.tpValue < position.price &&
                                     target.tpValue < position.tp;
            if (isValid)
            {
                //
                comment = "Target Manager Change TP ...";
                result = trader.Modify(
                    position.ticket,
                    position.sl,    // SL ...
                    target.tpValue, // TP ...
                    comment         //
                );

                //
                // Handle Alert ...
                if (result)
                {
                    //
                    message = "Target Manager Change TP of" + ToXString(position.type) + " Position (" + ToXString(position.ticket) + ") to: " + ToXString(target.tpValue) + " ...";
                    alert.SendAlert(message);
                }
            }
        }

        //
        return result;
    }

    //
    // Private ...
  private:
    //

    //
    // Required Event Handlers ...
    TOnStopLoss mStopLossEventHandlers[];
    TOnTakeProfit mTakeProfitEventHandlers[];
    TOnForceClose mOnForceCloseEventHandlers[];
};

//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-trade-manager.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XCXTradeManager
// Description: provides all Trade Management requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Classes/x-saherelm.x-data.collector.class.mq5"
#include "../Classes/x-saherelm.x-guard.class.mq5"
#include "../Classes/x-saherelm.x-target.class.mq5"
#include "../Classes/x-saherelm.x-poi.drawer.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Classes/x-saherelm.x-volume.class.mq5"

//
// Implementation ...
class XCTradeManager : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...
    XCAlert *alert;
    XCGuard *guard;
    XCTrade *trader;
    XCTarget *target;
    XCVolume *volume;
    XTimeTracker timeTracker;
    XCDataCollector *collector;

    //
    XCPOIDrawer *drawer;

    //
    TCheckForGuard checkForGuardEventListener;

    //
    // Constructor ...
    XCTradeManager(
        XCAlert *_alert,
        XCTrade *_trader,
        XCVolume *_volume,
        XCGuard *_guard,
        XCTarget *_target //
    )
    {
        //
        alert = _alert;
        guard = _guard;
        volume = _volume;
        trader = _trader;
        target = _target;

        //
        drawer = new XCPOIDrawer();

        //
        allowTrade = true;
        allowLongs = true;
        allowShorts = true;

        //
        reportTrades = true;
        reportSignals = true;
        reportProtector = true;
        reportAfterTradesBalance = true;

        //
        SaveTrades(false);

        //
        collector = new XCDataCollector();

        //
        // Setting Collector Path ...
        string mPath = (IsValid(alert.GetPrefix())
                            ? alert.GetPrefix() + "\\"
                            : "") +
                       trader.mAccount.GetCompany();
        collector.Path(mPath);
    }

    //
    // Deconstructor ...
    ~XCTradeManager()
    {
        //
        Clean(trades);

        //
        timeTracker.Clean();

        //
        delete drawer;
        ZeroMemory(drawer);

        //
        delete collector;
        ZeroMemory(collector);
    }

    //
    // Properties ...

    //
    bool SaveTrades()
    {
        return mSaveTrades;
    }

    //
    void SaveTrades(bool value)
    {
        mSaveTrades = value;
    }

    //
    bool SaveSignals()
    {
        return mSaveSignals;
    }

    //
    void SaveSignals(bool value)
    {
        mSaveSignals = value;
    }

    //
    bool SaveWins()
    {
        return mSaveWins;
    }

    //
    void SaveWins(bool value)
    {
        mSaveWins = value;
    }

    //
    bool SaveLosts()
    {
        return mSaveLosts;
    }

    //
    void SaveLosts(bool value)
    {
        mSaveLosts = value;
    }

    //
    bool SaveRestrictions()
    {
        return mSaveRestrictions;
    }

    //
    void SaveRestrictions(bool value)
    {
        mSaveRestrictions = value;
    }

    //
    // Setter(s) ...

    //
    void SetSymbolConfigurations(string symbolConfiguration)
    {
        //
        int count = 0;
        bool has = false;

        //
        // If Not Provided ...
        has = IsSpecifiedValid(symbolConfiguration);
        if (!has)
        {
            //
            AddDefaultSymbolConfiguration();
            return;
        }

        //
        // Try to Parse Data ...
        string symbolData[];
        count = SplitContent(
            symbolData,
            symbolConfiguration,
            "]" //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            AddDefaultSymbolConfiguration();

            //
            SpecifiedClean(symbolData);
            return;
        }

        //
        // Loop through Exists Symbol Data's ...
        for (int i = 0; i < count; i++)
        {
            //
            string iData = symbolData[i];

            //
            // Extract Data Parts ...
            string iParts[];
            int iPartsCount = SplitContent(
                iParts,
                iData,
                "|" //
            );
            has = IsValidSize(iPartsCount);
            if (has)
            {
                //
                // Determines Required Parts ...
                bool hasName = iPartsCount >= 1;
                bool hasSymbol = iPartsCount >= 2;
                bool hasPeriod = iPartsCount >= 3;
                bool hasSessionsDescriptor = iPartsCount >= 4;
                bool hasMaxAllowedSpred = iPartsCount >= 5;
                bool hasMaxAllowedPositios = iPartsCount >= 6;
                bool hasRestrictionDescriptor = iPartsCount >= 7;

                //
                // Parts IDX ...
                int partsIDX = 0;

                //
                // Extract Name ...
                string iName =
                    hasName
                        ? iParts[partsIDX]
                        : "";
                iName = EscapeString(iName);
                partsIDX++;

                //
                // Extract Symbol ...
                string iSymbol =
                    hasSymbol
                        ? iParts[partsIDX]
                        : _Symbol;
                iSymbol = EscapeString(iSymbol);
                iSymbol = NormalizeSymbol(iSymbol);
                partsIDX++;

                //
                // Extract Period ...
                ENUM_TIMEFRAMES iPeriod =
                    hasPeriod
                        ? ToPeriod(EscapeString(iParts[partsIDX]))
                        : _Period;
                iPeriod = NormalizePeriod(iPeriod);
                partsIDX++;

                //
                // Extract Sessions Descriptor ...
                string iSessionsDescriptor =
                    hasSessionsDescriptor
                        ? iParts[partsIDX]
                        : "";
                iSessionsDescriptor = EscapeString(iSessionsDescriptor);
                partsIDX++;

                //
                // Extract Max Allowed Sspread ...
                double iMaxAllowedSpread =
                    hasMaxAllowedSpred
                        ? (double)(EscapeString(iParts[partsIDX]))
                        : 25;
                partsIDX++;

                //
                // Extract Max Allowedd Positions ...
                int iMaxAllowedPositions =
                    hasMaxAllowedPositios
                        ? (int)(EscapeString(iParts[partsIDX]))
                        : 5;
                partsIDX++;

                //
                // Extract Restrictions Descriptor ...
                string iRestrictionsDescriptor =
                    hasRestrictionDescriptor
                        ? iParts[partsIDX]
                        : "";
                iRestrictionsDescriptor = EscapeString(iRestrictionsDescriptor);
                partsIDX++;

                //
                XSymbolTradeConfig iConfig;

                //
                // Initialize Config ...
                has = iConfig.Init(
                    iName,
                    iSymbol,
                    iPeriod,
                    iSessionsDescriptor,
                    iMaxAllowedSpread,
                    iMaxAllowedPositions,
                    iRestrictionsDescriptor //
                );
                if (has)
                {
                    AddOrUpdateSymbolConfiguration(iConfig);
                }

                //
                // Cleanup Resources ...
                iConfig.Clean();
            }

            //
            // Cleanup Resources ...
            SpecifiedClean(iParts);
        }

        //
        // Cleanup Resources ...
        SpecifiedClean(symbolData);
    }

    //
    int FillSymbolConfigurations(XSymbolTradeConfig &dest[])
    {
        //
        SpecifiedClean(dest);

        //
        Copy(
            symbolTradeConfigs,
            dest //
        );

        //
        int result = ArraySize(dest);

        //
        return result;
    }

    //
    void SetTradeReports(
        bool _reportTrades,
        bool _reportSignals,
        bool _reportProtector,
        bool _reportRestrictions,
        bool _reportAfterTradesBalance //
    )
    {
        //
        reportTrades = _reportTrades;
        reportSignals = _reportSignals;
        reportProtector = _reportProtector;
        reportRestrictions = _reportRestrictions;
        reportAfterTradesBalance = _reportAfterTradesBalance;
    }

    //
    void SetTradePermissions(
        bool _allowTrade,
        bool _allowLongs,
        bool _allowShorts //
    )
    {
        //
        allowTrade = _allowTrade;
        allowLongs = _allowLongs;
        allowShorts = _allowShorts;
    }

    //
    // Tools and Helpers ...

    /**
     * Do All Position Managing Senaros ...
     */
    void Manage()
    {
        //
        // Handle Restrictions ...
        HandleRestrictions();

        //
        // Managing Targets ...
        target.Manage();

        //
        // Check Contains Trades for Managing ...
        bool has = HasChild(trades);
        if (!has)
        {
            return;
        }

        //
        // Update Required Infos of Trades ...
        int count = ArraySize(trades);
        for (int i = 0; i < count; i++)
        {
            //
            has = trades[i].IsExecuted() &&
                  !trades[i].IsFinalized();
            if (!has)
            {
                continue;
            }

            //
            XPosition iPosition;
            has = trader.GetPosition(
                trades[i].positionID,
                iPosition //
            );
            if (!has)
            {
                continue;
            }

            //
            // Update Trade Data ...
            trades[i].swap = iPosition.swap;
            trades[i].profit = iPosition.profit;
        }

        //
        // Do Trade Protection ...
        Protect();
    }

    /**
     * Do All Position Protection Here ...
     */
    void Protect()
    {
        //
        string prefix = "Protector: ";
        int targetDistance = 5;

        //
        bool has = HasChild(trades);
        if (!has)
        {
            return;
        }

        //
        XPosition ownPosition[];
        XPosition ownLongPosition[];
        XPosition ownShortPosition[];

        //
        int count = ArraySize(trades);
        for (int i = 0; i < count; i++)
        {
            //
            has =
                trades[i].IsExecuted() &&
                !trades[i].IsFinalized();
            if (!has)
            {
                continue;
            }

            //
            XPosition iPosition;
            has = trader.GetPosition(
                trades[i].positionID,
                iPosition //
            );
            if (!has)
            {
                continue;
            }

            //
            AddRef(
                iPosition,
                ownPosition //
            );

            //
            if (IsLong(iPosition.type))
            {
                //
                AddRef(
                    iPosition,
                    ownLongPosition //
                );
            }
            else
            {
                //
                AddRef(
                    iPosition,
                    ownShortPosition //
                );
            }
        }

        //
        has = HasChild(ownPosition);
        if (!has)
        {
            //
            Clean(ownPosition);
            Clean(ownLongPosition);
            Clean(ownShortPosition);

            //
            return;
        }

        //
        XGuard guards[];
        int guardsCount = CheckForGuard(guards);
        bool hasGuard = IsValidSize(guardsCount);
        if (hasGuard)
        {
            //
            guard.DoGuards(guards);
        }

        //
        double swapSum = 0;
        double profitSum = 0;
        double commissionSum = 0;

        //
        // Calculate Required Data On Collection ...
        count = ArraySize(ownPosition);
        for (int i = 0; i < count; i++)
        {
            //
            XPosition iPosition = ownPosition[i];

            //
            swapSum += iPosition.swap;
            profitSum += iPosition.profit;
            commissionSum += iPosition.commission;
        }

        //
        // Do Collection Protectiong ...

        //
        // Handle Force Close At Time ...
        has = HandleForceCloseAtTime();
        if (has)
        {
            //
            Clean(ownPosition);
            Clean(ownLongPosition);
            Clean(ownShortPosition);

            //
            return;
        }

        //
        // Do Signle Protections ...
        for (int i = 0; i < count; i++)
        {
            //
            // Select Position ...
            XPosition iPosition = ownPosition[i];
            int idx = -1;
            has = HasTrade(
                idx,
                iPosition.ticket //
            );
            if (!has)
            {
                continue;
            }

            //
            bool isLong = IsLong(iPosition.type);
            double points = GetPoints(iPosition.symbol);

            //
            // Handle RF On Targets ...
            if (iPosition.profit > 0)
            {
                //
                // Handle Risk Free On Targets ...
                XTarget targets[];
                Copy(
                    trades[idx].signal.targets,
                    targets //
                );
                int targetsCount = ArraySize(targets);
                has = IsValidSize(targetsCount);
                if (has)
                {
                    //
                    for (int j = 0; j < targetsCount; j++)
                    {
                        //
                        XTarget jTarget = targets[j];

                        //
                        int appliedTargetIDX = -1;

                        //
                        // Check Target is Applied Before or Not ...
                        has =
                            !trades[idx].signal.isTargetApplied
                                ? true
                                : trades[idx].signal.appliedTargetIDX < j;
                        if (!has)
                        {
                            continue;
                        }

                        //
                        // Check Traget Validation ...
                        has =
                            iPosition.sl == 0
                                ? true
                            : isLong
                                ? iPosition.sl < jTarget.target
                                : iPosition.sl > jTarget.target;
                        if (!has)
                        {
                            continue;
                        }

                        //
                        double targetDelta = isLong
                                                 ? jTarget.target + (targetDistance * points)
                                                 : jTarget.target - (targetDistance * points);

                        //
                        // Can Risk Free ...
                        bool canRF =
                            jTarget.doRF &&
                            jTarget.target > 0 &&
                            (isLong
                                 ? jTarget.target > iPosition.entry
                                 : jTarget.target < iPosition.entry) &&
                            (isLong
                                 ? iPosition.price > targetDelta
                                 : iPosition.price < targetDelta);

                        //
                        // Can Partial Close ...
                        bool canPC =
                            jTarget.volumeMultiplier > 0 &&
                            jTarget.target > 0 &&
                            (isLong
                                 ? jTarget.target > iPosition.entry
                                 : jTarget.target < iPosition.entry) &&
                            (isLong
                                 ? iPosition.price > targetDelta
                                 : iPosition.price < targetDelta);
                        bool canRFPC = canRF ||
                                       canPC;
                        if (canRFPC)
                        {
                            //
                            // Handle Risk Free ...
                            bool isRFDone = false;
                            if (canRF)
                            {
                                //
                                double tp = iPosition.tp;
                                double sl = jTarget.target;
                                string comment = "RF On Target ...";
                                isRFDone = trader.Modify(
                                    iPosition.ticket,
                                    sl,
                                    tp,
                                    comment //
                                );
                            }

                            //
                            // Handle Partail Close ...
                            bool isPCDone = false;
                            if (canPC)
                            {
                                //
                                double _volume = jTarget.volumeMultiplier * iPosition.volume;
                                _volume = NormalizeVolume(_volume, iPosition.symbol);
                                string comment = "RF On Target ...";
                                isPCDone = trader.ClosePartial(
                                    iPosition.ticket,
                                    _volume,
                                    comment //
                                );
                            }

                            //
                            // Handle Report Action ...
                            bool isRFPCDone = isRFDone ||
                                              isPCDone;
                            if (isRFPCDone)
                            {
                                //
                                trades[idx].signal.appliedTargetIDX = j;
                                trades[idx].signal.isTargetApplied = true;

                                //
                                string message = prefix +
                                                 ToXString(iPosition.type) +
                                                 " Position: " +
                                                 ToXString(iPosition.ticket) +
                                                 (isRFDone ? " RF" : "") +
                                                 (isPCDone ? " PC" : "") +
                                                 " On Traget: " + ToXString(jTarget.target) +
                                                 " Successfully ...";
                                HandleReportProtector(message);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Check Can Analyse Market based on Configurations ...
     *
     * @param  symbol: Symbol
     * @param  period: Period
     * @param  time: Analysing Time
     *
     * @return ( bool )
     */
    bool CanAnalyse(
        string symbol,
        ENUM_TIMEFRAMES period,
        datetime time //
    )
    {
        //
        bool result = false;

        //
        // Finding Symbol Configuration Index ...
        int idx = FindSymbolConfigurationIndex(
            symbol,
            period //
        );

        //
        // If Not Exists ...
        if (!IsValidIndex(idx))
        {
            //
            result = true;
            return result;
        }

        //
        // If Exists ...

        //
        // Check Session is Active ...
        result = symbolTradeConfigs[idx].CanTrade(time);
        if (!result)
        {
            return result;
        }

        //
        // Check Symbol Restrictions ...
        result = symbolTradeConfigs[idx].IsRestricted(time);
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    /**
     * Check Signal Execution Conditions ...
     *
     * @param  signal: XSignal instance reference ...
     * @param  executionResult: ENUM_X_SIGNAL_EXECUTION_RESULT member reference ...
     *
     * @return ( bool )
     */
    bool CheckSignal(
        XSignal &signal,
        ENUM_X_SIGNAL_EXECUTION_RESULT &executionResult //
    )
    {
        //
        bool result = false;

        //
        // Cleanup ...
        executionResult = X_SIGNAL_EXECUTION_UNKNOWN;

        //
        // Validate Signal ...
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Reporting Signal ...
        if (reportSignals)
        {
            //
            string message = signal.GetMessage("Provided");
            alert.SendAlert(message);
        }

        //
        SaveSignal(signal);

        //
        // Check Trade Permission ...
        result = AllowTrade(signal.type);
        if (!result)
        {
            //
            result = false;
            executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;

            //
            return result;
        }

        //
        // Check Sessions and Restrictions ...
        result = CanAnalyse(
            signal.symbol,
            signal.period,
            signal.time //
        );
        if (!result)
        {
            //
            executionResult = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED;

            //
            return result;
        }

        //
        // Check Signal Spread ...
        int idx = FindSymbolConfigurationIndex(
            signal.symbol,
            signal.period //
        );
        if (IsValidIndex(idx))
        {
            //
            double spread = GetSpread(signal.symbol);
            result =
                symbolTradeConfigs[idx].maxAllowedSpread > 0
                    ? spread <= symbolTradeConfigs[idx].maxAllowedSpread
                    : true;
            if (!result)
            {
                //
                executionResult = X_SIGNAL_EXECUTION_FAILED_SPREAD;

                //
                return result;
            }

            //
            // Checking Max Allowed Positions ...
            if (symbolTradeConfigs[idx].maxAllowedPositions > 0)
            {
                //
                XPosition positions[];
                ENUM_X_POSITION_TYPES mType = IsLong(signal.type)
                                                  ? X_POSITION_TYPE_LONG
                                                  : X_POSITION_TYPE_SHORT;
                int positionsCount = trader.GetPositions(
                    positions,
                    symbolTradeConfigs[idx].symbol,
                    NULL, // All Providers ...
                    symbolTradeConfigs[idx].period,
                    mType,
                    true, // Filter By Magic ...
                    true  // Force Clean ...
                );
                result = !IsValidSize(positionsCount) ||
                         (IsValidSize(positionsCount) &&
                          positionsCount < symbolTradeConfigs[idx].maxAllowedPositions);

                //
                // Cleanup Resources ...
                SpecifiedClean(positions);
            }
        }

        //
        return result;
    }

    /**
     * Check Allow Trade or not ..
     *
     * @param  type: ENUM_POSITIO_TYPE
     *
     * @return ( bool )
     */
    bool AllowTrade(ENUM_POSITION_TYPE type)
    {
        //
        bool result = false;

        //
        result = allowTrade;
        if (!result)
        {
            return result;
        }

        //
        result =
            (IsLong(type) && allowLongs) ||
            (IsShort(type) && allowShorts);

        //
        return result;
    }

    /**
     * Execute Specified Signal ...
     *
     * @param  signal: XSignal instance reference ...
     * @param  conditions: XCATBEAStrategyConditions instance reference ...
     *
     * @return ( bool )
     */
    bool Execute(XSignal &signal)
    {
        //
        bool result = false;

        //
        result = signal.IsValid();
        if (!result)
        {
            return result;
        }

        //
        int idx = -1;
        bool isExists = HasTrade(
            idx,
            signal //
        );
        result = !isExists;
        if (!result)
        {
            return result;
        }

        //
        ENUM_X_SIGNAL_EXECUTION_RESULT executionResult;

        //
        // Checking Signal For Execution ...
        result = CheckSignal(
            signal,
            executionResult //
        );
        if (result)
        {
            //
            result = trader.ExecuteSignal(
                signal,
                executionResult //
            );
        }

        //
        // Alert Signal Execution ...
        if (reportTrades)
        {
            //
            string executionMessage = ToString(executionResult);

            //
            string message = NULL;
            if (result)
            {
                message = signal.GetMessage("Executed");
            }
            else
            {
                message = signal.GetMessage("Failed due (" + executionMessage + ")");
            }

            //
            alert.SendAlert(message);
        }

        //
        if (result)
        {
            //
            // Create Trade Struct ...
            XTradeInfo iTrade;

            //
            ulong positionID = trader.GetLastOpenPositionTicket();
            double commission = trader.GetPositionCommission(positionID);

            //
            iTrade.signal = signal;
            iTrade.positionID = positionID;
            iTrade.commission = commission;
            iTrade.signaledAt = signal.time;
            iTrade.executedAt = TimeCurrent();
            iTrade.conditions = signal.conditions;

            //
            AddRef(
                iTrade,
                trades //
            );
        }

        //
        return result;
    }

    /**
     * Retrieve Specified Positions ...
     *
     * @param positions: XPosition instance Reference Array, which holds Founded Positions ...
     * @param symbol: string, Specified Symbol Name ...
     * @param provider: string, Specified Provider Name ...
     * @param period: ENUM_TIMEFRAMES, Specified Period ...
     *
     * @return ( int )
     */
    int GetPositions(
        XPosition &positions[],
        string symbol,
        string provider,
        ENUM_TIMEFRAMES period //
    )
    {
        //
        int result = 0;

        //
        Clean(positions);

        //
        bool has =
            IsValid(symbol) &&
            IsValid(period) &&
            IsValid(provider);
        if (!has)
        {
            return result;
        }

        //
        result = trader.GetPositions(
            positions,
            symbol,
            provider,
            period,
            NULL, // All Types ...
            true, // Filter By Magic ...
            true  // Force Clean ...
        );

        //
        result = ArraySize(positions);

        //
        return result;
    }

    /**
     * Handle Signal Recieved ...
     *
     * @param  signal: XSignal
     */
    void HandleSignal(XSignal &signal)
    {
        //
        // Validate Signal ...
        if (!signal.IsValid())
        {
            return;
        }

        //
        // Check Signal ...
        bool isExecuted = false;
        ENUM_X_SIGNAL_EXECUTION_RESULT signalExecutionResult;
        bool canExecute = CheckSignal(
            signal,
            signalExecutionResult //
        );
        if (canExecute)
        {
            //
            HandleSignalVolume(signal);
            isExecuted = Execute(signal);
        }

        //
        // Draw Signal Object ...
        XCSignalObject *signalObj;
        bool has = drawer.DrawSignal(
            signal,
            signalObj,
            4 //
        );
        if (has)
        {
            mObjects.Add(signalObj);
        }
    }

    /**
     * Handle Position Take Profit ...
     *
     * @param  deal: XDeal instance reference ...
     */
    void HandleTP(const XDeal &deal)
    {
        //
        // Check Ticket Exists or not ...
        int idx = -1;
        bool isExists = HasTrade(
            idx,
            deal.positionId //
        );
        if (!isExists)
        {
            return;
        }

        //
        // Apply Required Data ...
        trades[idx].swap = deal.swap;
        trades[idx].profit = deal.profit;
        trades[idx].finalizedAt = TimeCurrent();
        trades[idx].finalize = XTRADE_FINAL_TP;

        //
        // Save Trade ...
        SaveWin(trades[idx]);
        SaveTrade(trades[idx]);

        //
        // Handle Balance Reporting ...
        string prefix = "TP: " + ToXString(trades[idx].profit);
        HandleReportBalance(prefix);

        //
        // Handle Symbol Config ...

        //
        int configIdx = FindSymbolConfigurationIndex(
            trades[idx].signal.symbol,
            trades[idx].signal.period //
        );
        isExists = IsValidIndex(configIdx);
        if (isExists)
        {
            //
            symbolTradeConfigs[configIdx].profits += 1;
            symbolTradeConfigs[configIdx].managedTPs += 1;
            symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
        }

        //
        RemoveTrade(idx);

        //
        target.HandleOnTakeProfitTriggered(deal);
    }

    /**
     * Handle Position Stop Loss ...
     *
     * @param  deal: XDeal instance reference ...
     */
    void HandleSL(const XDeal &deal)
    {
        //
        // Check Ticket Exists or not ...
        int idx = -1;
        bool isExists = HasTrade(
            idx,
            deal.positionId //
        );
        if (!isExists)
        {
            return;
        }

        //
        // Apply Required Data ...
        trades[idx].swap = deal.swap;
        trades[idx].profit = deal.profit;
        trades[idx].finalizedAt = TimeCurrent();
        trades[idx].finalize = XTRADE_FINAL_SL;

        //
        // Save Trade ...
        SaveTrade(trades[idx]);

        //
        // Handle Balance Reporting ...
        string prefix = "SL: " + ToXString(trades[idx].profit);
        HandleReportBalance(prefix);

        //
        // Handle Symbol Info ...

        //
        bool isInProfit = trades[idx].profit > 0;
        if (isInProfit)
        {
            SaveWin(trades[idx]);
        }
        else
        {
            SaveLost(trades[idx]);
        }

        //
        // Handle Symbol Config ...

        //
        int configIdx = FindSymbolConfigurationIndex(
            trades[idx].signal.symbol,
            trades[idx].signal.period //
        );
        isExists = IsValidIndex(configIdx);
        if (isExists)
        {
            //
            if (isInProfit)
            {
                //
                symbolTradeConfigs[configIdx].profits += 1;
                symbolTradeConfigs[configIdx].managedTPs += 1;
            }
            else
            {
                //
                symbolTradeConfigs[configIdx].losses += 1;
                symbolTradeConfigs[configIdx].managedSLs += 1;
            }

            //
            symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
        }

        //
        RemoveTrade(idx);

        //
        target.HandleOnStopLossTriggered(deal);
    }

    /**
     * Handle Position Force Close ...
     *
     * @param  deal: XDeal instance reference ...
     */
    void HandleForceClose(const XPosition &position)
    {
        //
        // Check Ticket Exists or not ...
        int idx = -1;
        bool isExists = HasTrade(
            idx,
            position.ticket //
        );
        if (!isExists)
        {
            return;
        }

        //
        bool isInProfit = position.profit > 0;

        //
        // Apply Required Data ...
        trades[idx].swap = position.swap;
        trades[idx].profit = position.profit;
        trades[idx].finalizedAt = TimeCurrent();
        trades[idx].commission = position.commission;
        trades[idx].finalize = isInProfit
                                   ? XTRADE_FINAL_CLOSE_IN_PROFIT
                                   : XTRADE_FINAL_CLOSE_IN_LOSE;

        //
        // Save Trade ...
        SaveTrade(trades[idx]);
        if (isInProfit)
        {
            SaveWin(trades[idx]);
        }
        else
        {
            SaveLost(trades[idx]);
        }

        //
        // Handle Balance Reporting ...
        string prefix = "Force Close " +
                        (isInProfit
                             ? "Profit"
                             : "Loss") +
                        ": " +
                        ToXString(trades[idx].profit);
        HandleReportBalance(prefix);

        //
        // Handle Symbol Config ...

        //
        int configIdx = FindSymbolConfigurationIndex(
            trades[idx].signal.symbol,
            trades[idx].signal.period //
        );
        isExists = IsValidIndex(configIdx);
        if (isExists)
        {
            //
            if (isInProfit)
            {
                //
                symbolTradeConfigs[configIdx].profits += 1;
                symbolTradeConfigs[configIdx].managedTPs += 1;
            }
            else
            {
                //
                symbolTradeConfigs[configIdx].losses += 1;
                symbolTradeConfigs[configIdx].managedSLs += 1;
            }

            //
            symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
        }

        //
        RemoveTrade(idx);

        //
        target.HandleOnPositionForceClosed(
            position.ticket,
            position,
            position.comment //
        );
    }

    /**
     * Handle All Symbols Config Force Close At Time ...
     *
     * @return ( bool )
     */
    bool HandleForceCloseAtTime()
    {
        //
        bool result = false;

        //
        // Check Symbol Config Exists ...
        result = HasChild(symbolTradeConfigs);
        if (!result)
        {
            return result;
        }

        //
        result = false;
        int count = ArraySize(symbolTradeConfigs);
        for (int i = 0; i < count; i++)
        {
            //
            // Check Can Force Closed At ...
            bool has = IsSpecifiedValid(symbolTradeConfigs[i].closeAllOpenTradesAt);
            if (!has)
            {
                continue;
            }

            //
            // Parse Date ...
            datetime forceCloseTime = ParseDateTimeFromTimeString(symbolTradeConfigs[i].closeAllOpenTradesAt);
            has = IsTimeInRange(
                      TimeCurrent(),
                      forceCloseTime //
                      ) &&
                  !symbolTradeConfigs[i].isForceCloseAtTime;
            if (!has)
            {
                continue;
            }

            //
            // Selecting Positions ...
            XTradeInfo iTrades[];
            int iTradesCount = FillSymbolConfigurationTrades(
                symbolTradeConfigs[i],
                iTrades //
            );
            has = IsValidSize(iTradesCount);
            if (!has)
            {
                //
                SpecifiedClean(iTrades);
                continue;
            }

            //
            // Converts Trades to Positions ...
            XPosition iPositions[];
            int iPositionsCount = ToXPositions(
                iTrades,
                iPositions //
            );
            has = IsValidSize(iPositionsCount);
            if (!has)
            {
                //
                SpecifiedClean(iTrades);
                SpecifiedClean(iPositions);
                continue;
            }

            //
            // Do Force Closing ...
            has = HandleForceCloseTrades(iPositions);
            if (has && !result)
            {
                result = true;
            }

            //
            // Cleanup Resources ...
            SpecifiedClean(iTrades);
            SpecifiedClean(iPositions);
        }

        //
        return result;
    }

    /**
     * Handle Position Partially Closed Event ...
     *
     * @param  ticket: ULONG ...
     * @param  profit: double ...
     * @param  comment: string ...
     */
    void HandlePartiallyClosed(
        const ulong ticket,
        const double profit,
        const string comment //
    )
    {
        //
        // Handle Balance Reporting ...
        string prefix = "Partially Closed: " + ToXString(ticket);

        //
        // Handle Balance Reporting ...
        HandleReportBalance(prefix);
    }

    /**
     * Handle Guard Events ...
     *
     * @param  action: ENUM_X_GUARD_ACTIONS
     * @param  positions: XPosition[]
     */
    void HandleGuardEvent(
        ENUM_X_GUARD_ACTIONS action,
        XPosition &positions[] //
    )
    {
    }

    /**
     * Handle Signal Executed Event ...
     *
     * @param  signal: XSignal, reference to Executed Signal ...
     */
    void HandleSignalExecuted(XSignal &signal)
    {
        target.AddExecutedSignal(signal);
    }

    //
    // Protected ...
  protected:
    //
    // Tools ...

    //
    string GetTradeFilePath(XTradeInfo &trade)
    {
        //
        string fileName = trade.GetFileName();

        //
        return GetTradeFilePath(fileName);
    }

    //
    string GetTradeFilePath(string fileName)
    {
        //
        string result = NULL;

        //
        result = collector
                     .GetFilePath("Trades" + "\\" + fileName);

        //
        return result;
    }

    //
    string GetSignalFilePath(string fileName)
    {
        //
        string result = NULL;

        //
        result = collector
                     .GetFilePath("Signals" + "\\" + fileName);

        //
        return result;
    }

    //
    string GetWinsFilePath(XTradeInfo &trade)
    {
        //
        string result = NULL;

        //
        result = collector
                     .GetFilePath("Conditions" + "\\" +
                                  trade.signal.symbol + "\\" +
                                  ToXString(trade.signal.period) + "_wins");

        //
        return result;
    }

    //
    string GetLostsFilePath(XTradeInfo &trade)
    {
        //
        string result = NULL;

        //
        result = collector
                     .GetFilePath("Conditions" + "\\" +
                                  trade.signal.symbol + "\\" +
                                  ToXString(trade.signal.period) + "_losts");

        //
        return result;
    }

    //
    string GetRestrictionsFilePath(string symbol)
    {
        //
        string result = NULL;

        //
        if (!IsValid(symbol))
        {
            return result;
        }

        //
        result = collector
                     .GetFilePath("Restrictions" + "\\" + symbol);

        //
        return result;
    }

    //
    void HandleReportBalance(string prefix = NULL)
    {
        //
        if (!reportAfterTradesBalance)
        {
            return;
        }

        //
        double balance = trader.mAccount.GetBalance();
        string currency = trader.mAccount.GetCurrency();

        //
        string message =
            (IsValid(prefix)
                 ? prefix + " | "
                 : "") +
            "Account Balance: " + ToXString(balance) + currency;
        alert.SendAlert(message);
    }

    //
    void HandleReportProtector(string message = NULL)
    {
        //
        if (!reportProtector)
        {
            return;
        }

        //
        alert.SendAlert(message);
    }

    /**
     * Handle All Exists Symbol Configurations Restrictions ...
     */
    void HandleRestrictions()
    {
        //
        // Count Symbol Configurations ...
        int count = ArraySize(symbolTradeConfigs);
        bool has = IsValidSize(count);
        if (!has)
        {
            return;
        }

        //
        // Handle Restrictions ...
        datetime cTime = TimeCurrent();
        bool isNewDay = timeTracker.IsNewDay();
        for (int i = 0; i < count; i++)
        {
            //
            // Reset Force Close At Time ...
            if (isNewDay)
            {
                symbolTradeConfigs[i].isForceCloseAtTime = false;
            }

            //
            // Handle Restrictions ...
            symbolTradeConfigs[i].HandleRestrictions(cTime);
        }
    }

    /**
     * Apply Signal Volume ...
     *
     * @param  signal: XSignal
     */
    void HandleSignalVolume(XSignal &signal)
    {
        //
        // Validate ...
        if (!signal.IsValid())
        {
            return;
        }

        //
        // Handle Volume Management ...
        double iVolume = volume.CalculateVolume(signal);
        bool has = iVolume > X_MIN_VOLUME &&
                   iVolume != signal.volume;
        if (has)
        {
            signal.volume = iVolume;
        }

        //
        // Here we Can Implement Custom Signalling Volume Management
        // based on Different Symbol Configurations ...
    }

    /**
     * Handle Force Close Trades ...
     */
    bool HandleForceCloseTrades(
        XPosition &positions[] //
    )
    {
        //
        bool result = false;

        //
        result = HasChild(positions);
        if (!result)
        {
            return result;
        }

        //
        string prefix = "Protector: ";
        string comment = "Force Close ...";

        //
        int count = ArraySize(positions);
        for (int i = 0; i < count; i++)
        {
            HandleForceClose(positions[i]);
        }

        //
        result = IsValidSize(count);
        if (result)
        {
            //
            string message = prefix + "Force Close (" + ToXString(count) + ") Trades at Specified Time ...";
            HandleReportProtector(message);
        }

        //
        return result;
    }

    /**
     * Check For Guards ...
     *
     * @param  dest: XGuard[]
     */
    int CheckForGuard(XGuard &dest[])
    {
        //
        int result = 0;

        //
        // Cleanup Dest ...
        SpecifiedClean(dest);

        //
        // Check For Guards ...
        if (checkForGuardEventListener != NULL)
        {
            //
            XPosition positions[];
            trader.GetPositions(positions);
            result = checkForGuardEventListener(dest, positions, 0);
        }

        //
        result = ArraySize(dest);

        //
        return result;
    }

    //
    // Save Trades ...
    bool SaveTrade(
        XTradeInfo &trade,
        bool includeSummary = true,
        bool includeSignal = true,
        bool includeConditions = true //
    )
    {
        //
        bool result = false;

        //
        if (!mSaveTrades)
        {
            return result;
        }

        //
        string filePath = GetTradeFilePath(trade);
        result = IsValid(filePath);
        if (!result)
        {
            return result;
        }

        //
        string content = trade.ToString(
            includeSummary,
            includeSignal,
            includeConditions //
        );

        //
        result = collector.Save(
            filePath,
            content //
        );

        //
        return result;
    }

    //
    // Save Signals ...
    bool SaveSignal(XSignal &signal)
    {
        //
        bool result = false;

        //
        // Check Signal Save is Enabled ...
        if (!mSaveSignals)
        {
            return result;
        }

        //
        // Check Signal Validation ...
        if (!signal.IsValid())
        {
            return result;
        }

        //
        // Retrieve Signal File Name ...
        string signalFileName = signal.GetFileName();
        result = IsValid(signalFileName);
        if (!result)
        {
            return result;
        }

        //
        // Retriev and Validate File Name ...
        string filePath = GetSignalFilePath(signalFileName);
        result = IsValid(filePath);
        if (!result)
        {
            return result;
        }

        //
        // Generate Content ...
        string content = "";
        content +=
            ToXString("----------------") + "\n" +
            ToXString("Signal: ") + "\n" +
            ToXString("----------------") + "\n" +
            signal.ToString() + "\n";
        content +=
            ToXString("----------------") + "\n" +
            ToXString("Conditions: ") + "\n" +
            ToXString("----------------") + "\n" +
            signal.conditions + "\n";

        //
        // Save Content to File ...
        result = collector.Save(
            filePath,
            content //
        );

        //
        // Return Result ...
        return result;
    }

    //
    bool SaveWin(XTradeInfo &trade)
    {
        //
        bool result = false;

        //
        if (!mSaveWins)
        {
            return result;
        }

        //
        string filePath = GetWinsFilePath(trade);
        result = IsValid(filePath);
        if (!result)
        {
            return result;
        }

        //
        // Preparing Conditions Content ...
        string content = trade.GetConditionsString();

        //
        result = collector.Append(
            filePath,
            content //
        );

        //
        return result;
    }

    //
    bool SaveLost(XTradeInfo &trade)
    {
        //
        bool result = false;

        //
        if (!mSaveLosts)
        {
            return result;
        }

        //
        string filePath = GetLostsFilePath(trade);
        result = IsValid(filePath);
        if (!result)
        {
            return result;
        }

        //
        // Preparing Conditions Content ...
        string content = trade.GetConditionsString();

        //
        result = collector.Append(
            filePath,
            content //
        );

        //
        return result;
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    CArrayObj mObjects;

    //
    bool allowTrade;
    bool allowLongs;
    bool allowShorts;

    //
    bool mSaveWins;
    bool mSaveLosts;
    bool mSaveTrades;
    bool mSaveSignals;
    bool mSaveRestrictions;

    //
    bool reportTrades;
    bool reportSignals;
    bool reportProtector;
    bool reportRestrictions;
    bool reportAfterTradesBalance;

    //
    XTradeInfo trades[];
    XSymbolTradeConfig symbolTradeConfigs[];

    /**
     * Find Executed Trade by Providing Position Ticket ...
     *
     * @param  index: int reference, holding founded item index ...
     * @param  ticket: ulong, Specified Position Ticket ...
     *
     * @return ( bool )
     */
    bool HasTrade(
        int &index,
        ulong ticket //
    )
    {
        //
        bool result = false;

        //
        index = -1;

        //
        result =
            ticket > 0 &&
            HasChild(trades);
        if (!result)
        {
            return result;
        }

        //
        int count = ArraySize(trades);
        for (int i = 0; i < count; i++)
        {
            //
            XTradeInfo iTrade = trades[i];

            //
            result =
                iTrade.HasSignal() &&
                iTrade.IsExecuted() &&
                iTrade.positionID == ticket;
            if (result)
            {
                //
                index = i;

                //
                iTrade.Clean();
                break;
            }

            //
            iTrade.Clean();
        }

        //
        result = IsValidIndex(index);

        //
        return result;
    }

    /**
     * Find Trade Item By Signal ...
     *
     * @param  index: int reference, holding founded item index ...
     *
     * @return ( bool )
     */
    bool HasTrade(
        int &index,
        XSignal &signal //
    )
    {
        //
        bool result = false;

        //
        index = -1;

        //
        result =
            signal.IsValid() &&
            HasChild(trades);
        if (!result)
        {
            return result;
        }

        //
        int count = ArraySize(trades);
        for (int i = 0; i < count; i++)
        {
            //
            XTradeInfo iTrade = trades[i];

            //
            result =
                iTrade.HasSignal() &&
                iTrade.signal.IsSameAs(signal);
            if (result)
            {
                //
                index = i;

                //
                iTrade.Clean();
                break;
            }

            //
            iTrade.Clean();
        }

        //
        result = IsValidIndex(index);

        //
        return result;
    }

    /**
     * Remove a Trade From List ...
     *
     * @param  index: int ...
     */
    void RemoveTrade(int index)
    {
        //
        bool has =
            HasChild(trades) &&
            IsValidIndex(index) &&
            index < ArraySize(trades);
        if (!has)
        {
            return;
        }

        //
        ArrayRemove(
            trades,
            index,
            1 //
        );
    }

    //
    void AddOrUpdateSymbolConfiguration(XSymbolTradeConfig &config)
    {
        //
        // Check Validation ...
        bool has = config.IsValid();
        if (!has)
        {
            return;
        }

        //
        // Check Index ...
        int idx = FindSymbolConfigurationIndex(config);
        has = IsValidIndex(idx);
        if (has)
        {
            //
            // Update Exists ...
            symbolTradeConfigs[idx] = config;
        }
        else
        {
            //
            // Add New ...
            AddRef(
                config,
                symbolTradeConfigs //
            );
        }
    }

    //
    void AddDefaultSymbolConfiguration()
    {
        //
        // Define new Configuration ...
        XSymbolTradeConfig iConfig;

        //
        // Check Initialization ...
        bool has = iConfig.Init(
            "Default",
            _Symbol,
            _Period //
        );

        //
        // Add Or Update ...
        if (has)
        {
            AddOrUpdateSymbolConfiguration(iConfig);
        }

        //
        // Clean Resources ...
        iConfig.Clean();
    }

    //
    int FindSymbolConfigurationIndex(XSymbolTradeConfig &config)
    {
        //
        int result = -1;

        //
        if (!config.IsValid() || !SpecifiedHasChild(symbolTradeConfigs))
        {
            return result;
        }

        //
        int count = ArraySize(symbolTradeConfigs);
        for (int i = 0; i < count; i++)
        {
            //
            if (symbolTradeConfigs[i].IsSame(config))
            {
                //
                result = i;
                break;
            }
        }

        //
        return result;
    }

    //
    int FillSymbolConfigurationTrades(
        XSymbolTradeConfig &config,
        XTradeInfo &dest[] //
    )
    {
        //
        int result = 0;

        //
        SpecifiedClean(dest);

        //
        // Check Trades ...
        int count = ArraySize(trades);
        if (!IsValidSize(count))
        {
            return result;
        }

        //
        // Validate Config ...
        if (!config.IsValid())
        {
            return result;
        }

        //
        // Find Config ...
        int idx = FindSymbolConfigurationIndex(config);
        if (!IsValidIndex(idx))
        {
            return result;
        }

        //
        bool has = false;
        string symbol = symbolTradeConfigs[idx].symbol;
        ENUM_TIMEFRAMES period = symbolTradeConfigs[idx].period;
        for (int i = 0; i < count; i++)
        {
            //
            has =
                trades[i].signal.symbol == symbol &&
                trades[i].signal.period == period &&
                trades[i].positionID > 0;
            if (!has)
            {
                continue;
            }

            //
            AddRef(
                trades[i],
                dest //
            );
        }

        //
        result = ArraySize(dest);

        //
        return result;
    }

    //
    int FindSymbolConfigurationIndex(
        string symbol,
        ENUM_TIMEFRAMES period //
    )
    {
        //
        int result = -1;

        //
        // Validate Args ...
        if (
            !IsSpecifiedValid(symbol) ||
            !IsSpecifiedValid(period) ||
            !HasChild(symbolTradeConfigs))
        {
            return result;
        }

        //
        int count = ArraySize(symbolTradeConfigs);
        for (int i = 0; i < count; i++)
        {
            //
            if (symbolTradeConfigs[i].symbol == symbol &&
                symbolTradeConfigs[i].period == period)
            {
                //
                result = i;
                break;
            }
        }

        //
        return result;
    }

    //
    // Tools ...

    //
    int ToXPositions(
        XTradeInfo &source[],
        XPosition &dest[] //
    )
    {
        //
        int result = 0;

        //
        SpecifiedClean(dest);

        //
        int count = ArraySize(source);
        bool has = IsValidSize(count);
        if (!has)
        {
            return result;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            XPosition iPosition;
            has = trader.GetPosition(
                source[i].positionID,
                iPosition);
            if (has)
            {
                //
                AddRef(
                    iPosition,
                    dest //
                );
            }

            //
            iPosition.Clean();
        }

        //
        result = ArraySize(dest);

        //
        return result;
    }

    //
    string EscapeString(string value)
    {
        //
        string result = value;

        //
        if (!IsSpecifiedValid(value))
        {
            return result;
        }

        //
        StringReplace(
            result,
            "[",
            "" //
        );

        //
        StringReplace(
            result,
            "]",
            "" //
        );

        //
        StringReplace(
            result,
            "|",
            "" //
        );

        //
        result = Trim(result);

        //
        return result;
    }
};

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-trade.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCTrade
// Description: provides all Trade requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-account.class.mq5"
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Libraries/x-saherelm.x-trade.lib.mq5"

//
// Definitions ...

//
// Overrides ...

//
// XCTrade Base Class ...
class XCTradeBase : public CTrade
{
  public:
    //
    // Modify Specific Position, by Specific Comment ...
    bool PositionModify(
        const string symbol,      // Symbol
        const double sl,          // Stop Loss
        const double tp,          // Take Profit
        const string comment = "" // Comment For Modify
    )
    {
        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // check position existence
        if (!SelectPosition(symbol))
        {
            return (false);
        }

        //
        // clean
        ClearStructures();

        //
        // setting request
        m_request.action = TRADE_ACTION_SLTP;
        m_request.symbol = symbol;
        m_request.magic = m_magic;
        m_request.sl = sl;
        m_request.tp = tp;
        m_request.position = PositionGetInteger(POSITION_TICKET);

        //
        // Specified Comment ...
        if (StringLen(comment) > 0)
        {
            m_request.comment = comment;
        }

        //
        // action and return the result
        return (OrderSend(m_request, m_result));
    }
    bool PositionModify(
        const ulong ticket,       // Position Ticket
        const double sl,          // Stop Loss
        const double tp,          // Take Profit
        const string comment = "" // Comment For Modify
    )
    {
        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // check position existence
        if (!PositionSelectByTicket(ticket))
        {
            return (false);
        }

        //
        // clean
        ClearStructures();

        //
        // setting request
        m_request.action = TRADE_ACTION_SLTP;
        m_request.position = ticket;
        m_request.symbol = PositionGetString(POSITION_SYMBOL);
        m_request.magic = m_magic;
        m_request.sl = sl;
        m_request.tp = tp;

        //
        // Specified Comment ...
        if (StringLen(comment) > 0)
        {
            m_request.comment = comment;
        }

        //
        // action and return the result
        return (OrderSend(m_request, m_result));
    }

    //
    // Close Specific Position, by Specific Comment ...
    bool PositionClose(
        const string symbol,      // Symbol
        const ulong deviation,    // Deviation
        const string comment = "" // Comment For Close
    )
    {
        //
        bool partial_close = false;
        int retry_count = 10;
        uint retcode = TRADE_RETCODE_REJECT;

        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // clean
        ClearStructures();

        //
        // check filling
        if (!FillingCheck(symbol))
        {
            return (false);
        }

        //
        do
        {
            //
            // check
            if (SelectPosition(symbol))
            {
                //
                if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    //
                    // prepare request for close BUY position
                    m_request.type = ORDER_TYPE_SELL;
                    m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
                }
                else
                {
                    //
                    // prepare request for close SELL position
                    m_request.type = ORDER_TYPE_BUY;
                    m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
                }
            }
            else
            {
                //
                // position not found
                m_result.retcode = retcode;
                return (false);
            }

            //
            // setting request
            m_request.action = TRADE_ACTION_DEAL;
            m_request.symbol = symbol;
            m_request.volume = PositionGetDouble(POSITION_VOLUME);
            m_request.magic = m_magic;
            m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
            m_request.position = PositionGetInteger(POSITION_TICKET);

            //
            // Specify Comment ...
            m_request.comment = comment;

            //
            // check volume
            double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
            if (m_request.volume > max_volume)
            {
                //
                m_request.volume = max_volume;
                partial_close = true;
            }
            else
            {
                partial_close = false;
            }

            //
            // hedging? just send order
            if (IsHedging())
            {
                return (OrderSend(m_request, m_result));
            }

            //
            // order send
            if (!OrderSend(m_request, m_result))
            {
                //
                if (--retry_count != 0)
                {
                    continue;
                }

                //
                if (retcode == TRADE_RETCODE_DONE_PARTIAL)
                {
                    m_result.retcode = retcode;
                }

                //
                return (false);
            }

            //
            //--- WARNING. If position volume exceeds the maximum volume allowed for deal,
            //--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely,
            //--- but partially. It is decreased by the maximum volume allowed for deal.
            if (m_async_mode)
            {
                break;
            }

            //
            retcode = TRADE_RETCODE_DONE_PARTIAL;
            if (partial_close)
            {
                Sleep(1000);
            }
        } while (partial_close);

        //
        // succeed
        return (true);
    }
    bool PositionClose(
        const ulong ticket,       // Position Ticket
        const ulong deviation,    // Deviation
        const string comment = "" // Comment For Close
    )
    {
        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // check position existence
        if (!PositionSelectByTicket(ticket))
        {
            return (false);
        }

        //
        string symbol = PositionGetString(POSITION_SYMBOL);

        //
        // clean
        ClearStructures();

        //
        // check filling
        if (!FillingCheck(symbol))
        {
            return (false);
        }

        //
        // check
        if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
            //
            // prepare request for close BUY position
            m_request.type = ORDER_TYPE_SELL;
            m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
        }
        else
        {
            //
            // prepare request for close SELL position
            m_request.type = ORDER_TYPE_BUY;
            m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
        }

        //
        // setting request
        m_request.action = TRADE_ACTION_DEAL;
        m_request.position = ticket;
        m_request.symbol = symbol;
        m_request.volume = PositionGetDouble(POSITION_VOLUME);
        m_request.magic = m_magic;
        m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;

        //
        // Specify Comment ...
        m_request.comment = comment;

        //
        // close position
        return (OrderSend(m_request, m_result));
    }

    //
    // Partial Close Specific Position, by Specific Comment ...
    bool PositionClosePartial(
        const string symbol,      // Symbol
        const double volume,      // Partial Closing Volume
        const ulong deviation,    // Deviation
        const string comment = "" // Comment For Close
    )
    {
        //
        uint retcode = TRADE_RETCODE_REJECT;

        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // for hedging mode only
        if (!IsHedging())
        {
            return (false);
        }

        //
        // clean
        ClearStructures();

        //
        // check filling
        if (!FillingCheck(symbol))
        {
            return (false);
        }

        //
        // check
        if (SelectPosition(symbol))
        {
            //
            if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
            {
                //
                // prepare request for close BUY position
                m_request.type = ORDER_TYPE_SELL;
                m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
            }
            else
            {
                //
                // prepare request for close SELL position
                m_request.type = ORDER_TYPE_BUY;
                m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
            }
        }
        else
        {
            //
            // position not found
            m_result.retcode = retcode;
            return (false);
        }

        //
        // check volume
        double position_volume = PositionGetDouble(POSITION_VOLUME);
        if (position_volume > volume)
        {
            position_volume = volume;
        }

        //
        // setting request
        m_request.action = TRADE_ACTION_DEAL;
        m_request.symbol = symbol;
        m_request.volume = position_volume;
        m_request.magic = m_magic;
        m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
        m_request.position = PositionGetInteger(POSITION_TICKET);

        //
        // Specifieng Comment ...
        m_request.comment = comment;

        //
        // hedging? just send order
        return (OrderSend(m_request, m_result));
    }
    bool PositionClosePartial(
        const ulong ticket,       // Position Ticket
        const double volume,      // Partial Closing Volume
        const ulong deviation,    // Deviation
        const string comment = "" // Comment For Close
    )
    {
        //
        // check stopped
        if (IsStopped(__FUNCTION__))
        {
            return (false);
        }

        //
        // for hedging mode only
        if (!IsHedging())
        {
            return (false);
        }

        //
        // check position existence
        if (!PositionSelectByTicket(ticket))
        {
            return (false);
        }

        //
        string symbol = PositionGetString(POSITION_SYMBOL);

        //
        // clean
        ClearStructures();

        //
        // check filling
        if (!FillingCheck(symbol))
        {
            return (false);
        }

        //
        // check
        if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
            //
            // prepare request for close BUY position
            m_request.type = ORDER_TYPE_SELL;
            m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
        }
        else
        {
            //
            // prepare request for close SELL position
            m_request.type = ORDER_TYPE_BUY;
            m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
        }

        //
        // check volume
        double position_volume = PositionGetDouble(POSITION_VOLUME);
        if (position_volume > volume)
        {
            position_volume = volume;
        }

        //
        // setting request
        m_request.action = TRADE_ACTION_DEAL;
        m_request.position = ticket;
        m_request.symbol = symbol;
        m_request.volume = position_volume;
        m_request.magic = m_magic;
        m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;

        //
        // Specifieng Comment ...
        m_request.comment = comment;

        //
        // close position
        return (OrderSend(m_request, m_result));
    }
};

//
// Implementation ...

//
// a Class For Manage Trades and Handle Trading Actions ...
class XCTrade : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...
    XCAccount *mAccount; // Account Info Provider

    //
    // Constructors ...
    XCTrade(
        int slippage,                       // Specify Slippage
        ulong magicNumber,                  // Specify Magic Number
        double maxAllowedSpread = 0,        // Max Allowed Spred for Opening Trades
        int maxAllowedPositions = 0,        // Max Allowed Positions
        double maxAllowedDrawdownFactor = 0 // Max Allowed Drawdown Factor
    )
    {
        //
        // Setting Props ...
        mSlippage = slippage;
        mMagicNumber = magicNumber;

        //
        mMaxAllowedSpread = maxAllowedSpread;
        mMaxAllowedPositions = maxAllowedPositions;
        mMaxAllowedDrawdownFactor = maxAllowedDrawdownFactor;

        //
        mAccount = new XCAccount();

        //
        // Instance Base Trader Class ...
        mTrader = new XCTradeBase();

        //
        // Configuring Base Trader Class ...
        mTrader.SetAsyncMode(false);
        mTrader.SetDeviationInPoints(mSlippage);
        mTrader.SetExpertMagicNumber(mMagicNumber);

        //
        // Setting Log Level ...
        mTrader.LogLevel(LOG_LEVEL_ERRORS);

        //
        // OnTrade Context ...

        //
        mDaysForRead = 1;
        mScanStarted = false;

        //
        // Reset On Trade Counter ...
        ResetOnTradeContext();

        //
        // Init On Trade Context ...
        InitOnTradeContext();
    }

    //
    // Deconstructor ...
    ~XCTrade()
    {
        //
        // Remove Pointer ...
        delete mTrader;
        delete mAccount;

        //
        ZeroMemory(mTrader);
        ZeroMemory(mAccount);

        //
        Clean(mOnModifyEventHandlers);
        Clean(mStopLossEventHandlers);
        Clean(mTakeProfitEventHandlers);
        Clean(mOnForceCloseEventHandlers);
        Clean(mDealsChangedEventHandlers);
        Clean(mOrdersChangedEventHandlers);
        Clean(mOnPartialCloseEventHandlers);
        Clean(mPositionsChangedEventHandlers);
        Clean(mTradeStateChangedEventHandlers);
    }

    //
    // Props ...

    //
    // Retrieve Slippage ...
    int GetSlippage()
    {
        return mSlippage;
    }

    //
    // Retrieve Magic Number ...
    ulong GetMagicNumber()
    {
        return mMagicNumber;
    }

    //
    double GetMaxAllowedSpread()
    {
        return mMaxAllowedSpread;
    }

    //
    int GetMaxAllowedPositions()
    {
        return mMaxAllowedPositions;
    }

    //
    void SetMaxAllowedPositions(int value)
    {
        //
        if (value < 0)
        {
            value = 0;
        }

        //
        mMaxAllowedPositions = value;
    }

    //
    double GetMaxAllowedDrawdownFactor()
    {
        return mMaxAllowedDrawdownFactor;
    }

    //
    // Add Event Listeners ...

    //
    void AddOnModifyPositionEventHandler(TOnModify handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnModifyEventHandlers
            //
        );
    }

    //
    void AddOnForceClosePositionEventHandler(TOnForceClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnForceCloseEventHandlers
            //
        );
    }

    //
    void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnPartialCloseEventHandlers
            //
        );
    }

    //
    // Add New Deals Changed Event Handler ...
    void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTradeStateChangedEventHandlers
            //
        );
    }

    //
    // Add New Deals Changed Event Handler ...
    void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mDealsChangedEventHandlers
            //
        );
    }

    //
    // Add New Orders Changed Event Handler ...
    void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOrdersChangedEventHandlers
            //
        );
    }

    //
    // Add New Positions Changed Event Handler ...
    void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mPositionsChangedEventHandlers
            //
        );
    }

    //
    // Add New Stop Loss Event Handler ...
    void AddOnStopLossEventHandler(TOnStopLoss handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mStopLossEventHandlers
            //
        );
    }

    //
    // Add New Stop Loss Event Handler ...
    void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mTakeProfitEventHandlers
            //
        );
    }

    //
    // Add New Signal Executed Event Handler ...
    void AddOnSignalExecutedEventHandler(TOnSignal handler)
    {
        //
        if (handler == NULL)
        {
            return;
        }

        //
        Add(
            handler,
            mOnSignalExecutedEventHandlers //
        );
    }

    //
    // Functions ...

    //
    // Handlers ...

    //
    // Process On Trade Requirement ...
    // this Only Used when we Have to Proccess OnTrade Event in EA(s) ...
    void HandleOnTradeEvent(
        XOnTradeHandlerState &result // Hold's Result
    )
    {
        //
        result.Clean();

        //
        if (mScanStarted)
        {
            //
            ProcessOnTradeContext(result);
        }
        else
        {
            //
            // Init Trade Context ...
            InitOnTradeContext();

            //
            return;
        }

        //
        // Here we Can Check the Start ...
        bool isValid =
            //
            result.IsValid() &&
            //
            !result.hasNewDeal &&
            !result.hasNewOrder &&
            !result.hasNewPosition &&
            !result.hasNewHistoryOrder
            //
            ;
        if (!isValid)
        {
            return;
        }

        //
        // State ...

        //
        // Notfy Trade State Changed Event ...
        NotifyTradeStateChangeEvent(result);

        //
        // Deals ...
        if (result.hasNewDeal)
        {
            //
            // Notfy Deals Changed Event ...
            NotifyDealsChangeEvent(result.newDeals);

            //
            // Retrieve Last Deal ...
            XDeal deals[];
            int dealsCount = GetDeals(deals);
            if (dealsCount > 0)
            {
                //
                XDeal lastDeal = deals[0];

                //
                if (lastDeal.reason == DEAL_REASON_TP)
                {
                    NotifyTakeProfitEvent(lastDeal);
                }
                else if (lastDeal.reason == DEAL_REASON_SL)
                {
                    NotifyStopLossEvent(lastDeal);
                }
            }
        }

        //
        // Orders ...
        if (result.hasNewOrder || result.hasNewHistoryOrder)
        {
            //
            // Notfy Orders Changed Event ...
            NotifyOrdersChangeEvent(result.newOrders + result.newHistoryOrders);
        }

        //
        // Positions ...
        if (result.hasNewPosition)
        {
            //
            // Notfy Positions Changed Event ...
            NotifyPositionsChangeEvent(result.newPositions);
        }
    }

    //
    // This Must be Called Every time OnTrade Event Happening ...
    // this Used for Implementing Custom Senarios ...
    XOnTradeHandlerState HandleOnTrade()
    {
        //
        // Handle OnTrade Event ...
        XOnTradeHandlerState state;
        HandleOnTradeEvent(state);
        if (
            !state.hasNewDeal &&
            !state.hasNewOrder &&
            !state.hasNewPosition &&
            !state.hasNewHistoryOrder)
        {
            return state;
        }

        //
        // State ...

        //
        // Notfy Trade State Changed Event ...
        NotifyTradeStateChangeEvent(state);

        //
        // DEALS ...
        if (state.hasNewDeal)
        {
            //
            // Notfy Deals Changed Event ...
            NotifyDealsChangeEvent(state.newDeals);

            //
            // Retrieve Last Deal ...
            XDeal deals[];
            GetDeals(deals);
            int dealsCount = ArraySize(deals);
            if (dealsCount > 0)
            {
                //
                XDeal lastDeal = deals[0];

                //
                if (lastDeal.reason == DEAL_REASON_TP)
                {
                    NotifyTakeProfitEvent(lastDeal);
                }
                else if (lastDeal.reason == DEAL_REASON_SL)
                {
                    NotifyStopLossEvent(lastDeal);
                }
            }
        }

        //
        // ORDERS ...
        if (state.hasNewOrder || state.hasNewHistoryOrder)
        {
            //
            // Notfy Orders Changed Event ...
            NotifyOrdersChangeEvent(state.newOrders + state.newHistoryOrders);
        }

        //
        // POSITIONS ...
        if (state.hasNewPosition)
        {
            //
            // Notfy Positions Changed Event ...
            NotifyPositionsChangeEvent(state.newPositions);
        }

        //
        return state;
    }

    //
    // Trade Actions ...

    //
    // Main ...

    //
    // Policies ...

    //
    // Check Acocunt Balance is Allow new Trades or not ...
    bool CheckEquityForTrade()
    {
        //
        bool result = false;

        //
        if (mMaxAllowedDrawdownFactor <= 0)
        {
            //
            result = true;
            return result;
        }

        //
        double equity = mAccount.GetEquity();
        double balance = mAccount.GetBalance();

        //
        double selectedBalance = MathMax(equity, balance) - MathMin(equity, balance);

        //
        double maxAllowedBalanceDrawDown = GetMaxAllowedEquity();

        //
        result = equity > balance ||
                 (equity <= balance &&
                  selectedBalance <= maxAllowedBalanceDrawDown);

        //
        return result;
    }

    //
    // Check Max Allowed Positions Policy for Trade ...
    bool CheckPositionsForTrade()
    {
        //
        bool result = false;

        //
        if (mMaxAllowedPositions <= 0)
        {
            //
            result = true;
            return result;
        }

        //
        XPosition positions[];
        int positionsCount = GetPositions(positions);
        if (positionsCount <= 0)
        {
            //
            result = true;
            return result;
        }

        //
        result = positionsCount < mMaxAllowedPositions;

        //
        return result;
    }

    //
    // Check Spread is Ok for Executing Specific Signal ...
    bool CheckSpreadForSignalExecution(XSignal &mSignal)
    {
        //
        bool result = false;

        //
        bool isSpreadCheckEnable = mMaxAllowedSpread > 0;
        if (!isSpreadCheckEnable)
        {
            result = true;
        }
        else
        {
            //
            // Retrieve Signal Spread ...
            double spread = GetSpread(mSignal.symbol);
            result = spread <= mMaxAllowedSpread;
        }

        //
        return result;
    }

    //
    // Check Account Policy ...
    bool CheckAccountPolicy(
        XSignal &mSignal,                     // For Executing Signal
        ENUM_X_SIGNAL_EXECUTION_RESULT &state // State Holder
    )
    {
        //
        bool result = false;

        //
        // Check Positions For Trade ...
        result = CheckPositionsForTrade();
        if (!result)
        {
            //
            state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
            return result;
        }

        //
        // Check Equity For Trade ...
        result = CheckEquityForTrade();
        if (!result)
        {
            //
            state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
            return result;
        }

        //
        // Check Spread for Trade ...
        result = CheckSpreadForSignalExecution(mSignal);
        if (!result)
        {
            //
            state = X_SIGNAL_EXECUTION_FAILED_SPREAD;
            return result;
        }

        //
        return result;
    }

    //
    // Execute a Signal ...
    bool ExecuteSignal(
        XSignal &mSignal,                               // Signal
        ENUM_X_SIGNAL_EXECUTION_RESULT &state,          // Result
        ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
        datetime expiration = NULL,                     // order expiration time
        bool checkPolicies = true                       // Check Account Policies
    )
    {
        //
        bool result = false;
        state = X_SIGNAL_EXECUTION_UNKNOWN;

        //
        double currentEntry = GetEntry(
            mSignal.symbol,
            mSignal.type
            //
        );
        bool isLong = IsLong(mSignal.type);

        //
        if (!mSignal.IsValid())
        {
            //
            state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
            return result;
        }

        //
        // Prepare Signal Comment ...
        string mSComment = mSignal.GenerateComment();
        if (!Contains(mSComment, mSignal.comment))
        {
            mSignal.comment += mSComment;
        }

        //
        // Handle Market Execution ...
        if (mSignal.mode == X_ORDER_MODE_MARKET)
        {
            //
            // Do Apply Policies here ...
            bool hasPolicy = !checkPolicies
                                 ? true
                                 : CheckAccountPolicy(mSignal, state);
            if (!hasPolicy)
            {
                result = false;
            }
            else
            {
                //
                if (isLong)
                {
                    //
                    result = Buy(
                        mSignal.symbol,
                        mSignal.period,
                        mSignal.volume,
                        mSignal.entry,
                        mSignal.sl,
                        mSignal.tp,
                        mSignal.comment
                        //
                    );
                }
                else
                {
                    //
                    result = Sell(
                        mSignal.symbol,
                        mSignal.period,
                        mSignal.volume,
                        mSignal.entry,
                        mSignal.sl,
                        mSignal.tp,
                        mSignal.comment
                        //
                    );
                }
            }
        }
        //
        // Handle Stop Execution ...
        else if (mSignal.mode == X_ORDER_MODE_STOP)
        {

            //
            // Check Conditions ...

            //
            // Check Price ...
            result = isLong
                         ? mSignal.entry > currentEntry
                         : mSignal.entry < currentEntry;
            if (!result)
            {
                //
                state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
                return result;
            }

            //
            // Execute Order ...
            if (isLong)
            {
                //
                result = BuyStop(
                    mSignal.symbol,
                    mSignal.period,
                    mSignal.volume,
                    mSignal.entry,
                    mSignal.sl,
                    mSignal.tp,
                    lifetime,
                    expiration,
                    mSignal.comment
                    //
                );
            }
            else
            {
                //
                result = SellStop(
                    mSignal.symbol,
                    mSignal.period,
                    mSignal.volume,
                    mSignal.entry,
                    mSignal.sl,
                    mSignal.tp,
                    lifetime,
                    expiration,
                    mSignal.comment
                    //
                );
            }
        }
        //
        // Handle Limit Execution ...
        else if (mSignal.mode == X_ORDER_MODE_LIMIT)
        {
            //
            // Check Conditions ...

            //
            // Check Price ...
            result = isLong
                         ? mSignal.entry < currentEntry
                         : mSignal.entry > currentEntry;
            if (!result)
            {
                //
                state = X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR;
                return result;
            }

            //
            // Execute Order ...
            if (isLong)
            {
                //
                result = BuyLimit(
                    mSignal.symbol,
                    mSignal.period,
                    mSignal.volume,
                    mSignal.entry,
                    mSignal.sl,
                    mSignal.tp,
                    lifetime,
                    expiration,
                    mSignal.comment
                    //
                );
            }
            else
            {
                //
                result = SellLimit(
                    mSignal.symbol,
                    mSignal.period,
                    mSignal.volume,
                    mSignal.entry,
                    mSignal.sl,
                    mSignal.tp,
                    lifetime,
                    expiration,
                    mSignal.comment
                    //
                );
            }
        }

        //
        // Handle Ticket ...
        if (result)
        {
            //
            state = X_SIGNAL_EXECUTION_SUCCEED;

            //
            ulong ticket;
            if (mSignal.mode == X_ORDER_MODE_MARKET)
            {
                ticket = GetLastOpenPositionTicket();
            }
            else
            {
                ticket = GetLastPlacedOrderTicket();
            }

            //
            mSignal.positionId = ticket;

            //
            // Notify Signal Execution ...
            if (mSignal.mode == X_ORDER_MODE_MARKET)
            {
                NotifySignalExecuted(mSignal);
            }
        }

        //
        return result;
    }

    //
    // Execute a Collection Of Signals ...
    int ExecuteSignals(
        XSignal &signals[],                             // Collection of Signal for Execution
        ENUM_X_SIGNAL_EXECUTION_RESULT &states[],       // Signal Execution Result
        ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
        datetime expiration = NULL,                     // order expiration time
        bool checkPolicies = true                       // Check Account Policies
    )
    {
        //
        int result = 0;

        //
        Clean(states);

        //
        int signalsCount = ArraySize(signals);
        if (signalsCount <= 0)
        {
            return result;
        }

        //
        ArrayResize(states, signalsCount);
        ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
        for (int i = 0; i < signalsCount; i++)
        {
            //
            bool isExecuted = ExecuteSignal(
                signals[i],
                states[i],
                lifetime,
                expiration,
                checkPolicies
                //
            );

            //
            if (isExecuted)
            {
                result++;
            }
        }

        //
        return result;
    }

    //
    int ExecuteSpecifiedSignals(
        XSignal &signals[],                             // Collection of Signal for Execution
        XSignal &notExecuteds[],                        // Not Executed Signal
        ENUM_X_SIGNAL_EXECUTION_RESULT &states[],       // Signal Execution Result
        ENUM_ORDER_TYPE_TIME lifetime = ORDER_TIME_GTC, // Order LifeTime
        datetime expiration = NULL,                     // order expiration time
        bool checkPolicies = true                       // Check Account Policies
    )
    {
        //
        int result = 0;

        //
        Clean(states);
        Clean(notExecuteds);

        //
        int signalsCount = ArraySize(signals);
        if (signalsCount <= 0)
        {
            return result;
        }

        //
        ArrayResize(states, signalsCount);
        ArrayInitialize(states, X_SIGNAL_EXECUTION_UNKNOWN);
        for (int i = 0; i < signalsCount; i++)
        {
            //
            bool isExecuted = ExecuteSignal(
                signals[i],
                states[i],
                lifetime,
                expiration
                //
            );

            //
            if (isExecuted)
            {
                result++;
            }
            else
            {
                //
                AddRef(
                    signals[i],
                    notExecuteds //
                );
            }
        }

        //
        return result;
    }

    //
    // Regular Trade Actions ...

    //
    // Buy ...

    //
    // Force Open a Buy/Long Position ...
    bool Buy(
        string symbol,          // Specified Symbol
        ENUM_TIMEFRAMES period, // Specified Trade's Period
        double volume,          // position volume
        double price,           // execution price
        double sl = 0,          // stop loss price
        double tp = 0,          // take profit price
        string comment = NULL   // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.Buy(
            volume,
            symbol,
            price,
            sl,
            tp,
            comment
            //
        );

        //
        return result;
    }

    //
    // Send BUY LIMIT order ...
    bool BuyLimit(
        string symbol,                                  // Specified Symbol
        ENUM_TIMEFRAMES period,                         // Specified Trade's Period
        double volume,                                  // order volume
        double price,                                   // order price
        double sl = 0.0,                                // stop loss price
        double tp = 0.0,                                // take profit price
        ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
        datetime expiration = 0,                        // order expiration time
        string comment = NULL                           // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.BuyLimit(
            volume,
            price,
            symbol,
            sl,
            tp,
            typeTime,
            expiration,
            comment
            //
        );

        //
        return result;
    }

    //
    // Send BUY STOP order  ...
    bool BuyStop(
        string symbol,                                  // Specified Symbol
        ENUM_TIMEFRAMES period,                         // Specified Trade's Period
        double volume,                                  // order volume
        double price,                                   // order price
        double sl = 0.0,                                // stop loss price
        double tp = 0.0,                                // take profit price
        ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
        datetime expiration = 0,                        // order expiration time
        string comment = NULL                           // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.BuyStop(
            volume,
            price,
            symbol,
            sl,
            tp,
            typeTime,
            expiration,
            comment
            //
        );

        //
        return result;
    }

    //
    // Sell ...

    //
    // Force Open a Sell/Short Position ...
    bool Sell(
        string symbol,          // Specified Symbol
        ENUM_TIMEFRAMES period, // Specified Trade's Period
        double volume,          // position volume
        double price,           // execution price
        double sl = 0,          // stop loss price
        double tp = 0,          // take profit price
        string comment = NULL   // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.Sell(
            volume,
            symbol,
            price,
            sl,
            tp,
            comment
            //
        );

        //
        return result;
    }

    //
    // Send SELL LIMIT order ...
    bool SellLimit(
        string symbol,                                  // Specified Symbol
        ENUM_TIMEFRAMES period,                         // Specified Trade's Period
        double volume,                                  // order volume
        double price,                                   // order price
        double sl = 0.0,                                // stop loss price
        double tp = 0.0,                                // take profit price
        ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
        datetime expiration = 0,                        // order expiration time
        string comment = NULL                           // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.SellLimit(
            volume,
            price,
            symbol,
            sl,
            tp,
            typeTime,
            expiration,
            comment
            //
        );

        //
        return result;
    }

    //
    // Send SELL STOP order ...
    bool SellStop(
        string symbol,                                  // Specified Symbol
        ENUM_TIMEFRAMES period,                         // Specified Trade's Period
        double volume,                                  // order volume
        double price,                                   // order price
        double sl = 0.0,                                // stop loss price
        double tp = 0.0,                                // take profit price
        ENUM_ORDER_TYPE_TIME typeTime = ORDER_TIME_GTC, // order lifetime
        datetime expiration = 0,                        // order expiration time
        string comment = NULL                           // comment
    )
    {
        //
        bool result = false;

        //
        ENUM_TIMEFRAMES mPeriod = ExtractPeriod(comment);
        if (!IsValid(comment) ||
            !IsValid(mPeriod))
        {
            //
            string mPeriodStr = GeneratePeriodTag(period);

            //
            comment += mPeriodStr;
        }

        //
        result = mTrader.SellStop(
            volume,
            price,
            symbol,
            sl,
            tp,
            typeTime,
            expiration,
            comment
            //
        );

        //
        return result;
    }

    //
    // Check a Position is Exists Open or not ...
    bool IsOpen(
        ulong ticket // Specified Position Ticket
    )
    {
        //
        bool result = false;

        //
        result = mPositionInfo
                     .SelectByTicket(ticket);

        //
        return result;
    }

    //
    // Modify Position ...
    bool Modify(
        const ulong ticket,   // position ticket
        double sl = 0,        // stop loss price
        double tp = 0,        // take profit
        string comment = NULL // comment
    )
    {
        //
        bool result = false;

        //
        // Validate Args ...
        XPosition position;
        result =
            GetPosition(
                ticket,
                position //
                ) &&
            (tp >= 0 || sl >= 0);
        if (!result)
        {
            return result;
        }

        //
        // Use Position Comment when there isn't new One ...
        if (!IsValid(comment) == 0)
        {
            //
            // if there isn't provided Comment
            // Use Position Comment for default ...
            comment = "Modify " + GetPositionComment(ticket);
        }

        //
        result = mTrader.PositionModify(
            ticket,
            sl,
            tp,
            comment
            //
        );
        if (result)
        {
            //
            NotifyModifyEvent(
                ticket,
                position.profit,
                comment //
            );
        }

        //
        return result;
    }

    //
    // Close Position By Ticket ...
    bool Close(
        ulong ticket,         // Position Ticket
        string comment = NULL // Close Position By Specific Comment
    )
    {
        //
        bool result = false;

        //
        // Check Position Open ...
        XPosition position;
        result = GetPosition(
            ticket,
            position //
        );
        if (!result)
        {
            return result;
        }

        //
        // Use Default Position Comment if it's Not Provided ...
        if (!IsValid(comment))
        {
            comment = "Close " + GetPositionComment(ticket);
        }

        //
        // Close Position By Specific Comment ...
        result = mTrader.PositionClose(
            ticket,
            mSlippage,
            comment
            //
        );
        if (result)
        {
            //
            NotifyForceCloseEvent(
                ticket,
                position,
                comment //
            );
        }

        //
        return result;
    }

    //
    // Close Specified Positions ...
    int Close(
        string comment = NULL,             // Close Position By Specific Comment
        string symbol = NULL,              // Trading Symbol
        string provider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES period = NULL,     // Trading Period
        ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
        bool filterByMagic = true          // Get Only Self Open Positions
    )
    {
        //
        int result = 0;

        //
        XPosition positions[];
        int positionsCount = GetPositions(
            positions,
            symbol,
            provider,
            period,
            type,
            filterByMagic);
        if (positionsCount <= 0)
        {
            return result;
        }

        //
        for (int i = 0; i < positionsCount; i++)
        {
            //
            XPosition iPosition = positions[i];

            //
            bool isClosed = Close(
                iPosition.ticket,
                comment
                //
            );
            if (isClosed)
            {
                result++;
            }
        }

        //
        return result;
    }

    //
    // Close a Collection of Positions ...
    int Close(
        XPosition &positions[], // Collection of Positions
        string comment = NULL   // Close Comment
    )
    {
        //
        int result = 0;

        //
        int positionsCount = ArraySize(positions);
        if (positionsCount <= 0)
        {
            return result;
        }

        //
        for (int i = 0; i < positionsCount; i++)
        {
            //
            XPosition iPosition = positions[i];

            //
            bool isClosed = Close(
                iPosition.ticket,
                comment
                //
            );
            if (isClosed)
            {
                result++;
            }
        }

        //
        return result;
    }

    //
    // Close Partial By Ticket ...
    bool ClosePartial(
        ulong ticket,         // Position Ticket
        double volume,        // Volume Size for Partial Closing
        string comment = NULL // Close Position By Specific Comment
    )
    {
        //
        bool result = false;

        //
        XPosition position;
        result = GetPosition(
            ticket,
            position //
        );
        if (!result)
        {
            return result;
        }

        //
        // Use Default Partial Close Comment ...
        if (!IsValid(comment))
        {
            //
            comment += "PClose " + GetPositionComment(ticket);
        }

        //
        result = mTrader.PositionClosePartial(
            ticket,
            volume,
            mSlippage,
            comment
            //
        );
        if (result)
        {
            //
            NotifyPartialCloseEvent(
                ticket,
                position.profit,
                comment //
            );
        }

        //
        return result;
    }

    //
    // Cancel Specific Order ...
    bool CancelOrder(ulong ticket)
    {
        return mTrader
            .OrderDelete(ticket);
    }

    //
    // Cancel Specific Orders ...
    int CancelOrders(
        XOrder &orders[] // Specified Orders ...
    )
    {
        //
        int result = 0;

        //
        int ordersCount = ArraySize(orders);
        if (ordersCount <= 0)
        {
            return result;
        }

        //
        for (int i = 0; i < ordersCount; i++)
        {
            //
            XOrder iOrder = orders[i];

            //
            bool isDeleted = mTrader
                                 .OrderDelete(iOrder.ticket);
            if (isDeleted && !result)
            {
                result++;
            }
        }

        //
        return result;
    }

    //
    // Extractors ...

    //
    // Positions ...

    //
    // Calculate Positions Profit ...
    double Profit(
        string symbol = NULL,              // Trading Symbol
        string provider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES period = NULL,     // Trading Period
        ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
        bool filterByMagic = true          // Get Only Self Open Positions
    )
    {
        //
        double result = 0;

        //
        // Retrieve Positions ...
        XPosition positions[];
        int positionsCount = GetPositions(
            positions,
            symbol,
            provider,
            period,
            type,
            filterByMagic
            //
        );
        if (positionsCount <= 0)
        {
            return result;
        }

        //
        for (int i = 0; i < positionsCount; i++)
        {
            result += positions[i].profit;
        }

        //
        return result;
    }

    //
    // Retrieve Specific Position by Index ...
    bool GetPositionByIndex(
        int index,        // Specified Index
        XPosition &result // Hold's Result
    )
    {
        //
        bool isInited = result
                            .ByIndex(index);

        //
        return isInited;
    }

    //
    // Retrieve Specific Position by Ticket ...
    bool GetPosition(
        ulong ticket,     // Specified Ticket
        XPosition &result // Hold's Result
    )
    {
        //
        bool isInited = result
                            .ByTicket(ticket);

        //
        return isInited;
    }

    //
    double GetPositionCommission(
        ulong ticket // Specified Ticket
    )
    {
        //
        double result = 0;

        //
        XPosition position;
        bool hasPosition = GetPosition(
            ticket,
            position //
        );
        if (!hasPosition)
        {
            return result;
        }

        //
        XDeal deals[];
        int dealsCount = GetDeals(
            deals,
            position.symbol,
            position.provider,
            position.period,
            NULL,
            DEAL_ENTRY_IN //
        );
        if (IsValidSize(dealsCount))
        {
            //
            for (int i = 0; i < dealsCount; i++)
            {
                //
                XDeal iDeal = deals[i];
                if (iDeal.positionId == ticket)
                {
                    //
                    result = iDeal.commission;
                    break;
                }
            }
        }

        //
        return result;
    }

    //
    // Retrieve Positions ...
    int GetPositions(
        XPosition &result[],               // Hold's Result
        string symbol = NULL,              // Trading Symbol
        string provider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES period = NULL,     // Trading Period
        ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
        bool filterByMagic = true,         // Get Only Self Open Positions
        bool forceClean = true             // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        // Count Positions ...
        int itemsCount = PositionsTotal();
        if (itemsCount <= 0)
        {
            return mResult;
        }

        //
        // Loop Through all Positions ...
        for (int i = itemsCount - 1; i >= 0; i--)
        {
            //
            XPosition iItem;
            bool isLoaded = iItem.ByIndex(i);
            if (!isLoaded)
            {
                continue;
            }

            //
            // Now we have to Check Given Filters ...
            bool isFiltersPassed =
                iItem.IsFiltersPassed(
                    symbol,
                    provider,
                    period,
                    type,
                    mMagicNumber,
                    filterByMagic
                    //
                );
            if (!isFiltersPassed)
            {
                continue;
            }

            //
            // Add Filtered Item into Result ...
            AddRef(
                iItem,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    void GetPositions(
        XPosition &longs[],            // Hold's Longs Result
        XPosition &shorts[],           // Hold's Shorts Result
        string symbol = NULL,          // Trading Symbol
        string provider = NULL,        // Signal Provider
        ENUM_TIMEFRAMES period = NULL, // Trading Period
        bool filterByMagic = true,     // Get Only Self Open Positions
        bool forceClean = true         // Clean Result Array
    )
    {
        //
        // Retrieve Long Positions ...
        GetPositions(
            longs,
            symbol,
            provider,
            period,
            X_POSITION_TYPE_LONG,
            filterByMagic,
            forceClean //
        );

        //
        // Retrieve Short Positions ...
        GetPositions(
            shorts,
            symbol,
            provider,
            period,
            X_POSITION_TYPE_SHORT,
            filterByMagic,
            forceClean //
        );
    }

    //
    // Select In Profit Positions ...
    int GetInProfitPositions(
        XPosition &result[],                          // Hold's Result
        string symbol = NULL,                         // Trading Symbol
        string provider = NULL,                       // Signal Provider
        ENUM_TIMEFRAMES period = NULL,                // Trading Period
        ENUM_X_POSITION_TYPES type = NULL,            // Long (Buy) or Short (Sell)
        ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
        bool filterByMagic = true,                    // Get Only Self Open Positions
        bool forceClean = true                        // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        XPosition positions[];
        int positionsCount = GetPositions(
            positions,
            symbol,
            provider,
            period,
            type,
            filterByMagic,
            forceClean);
        if (positionsCount <= 0)
        {
            return mResult;
        }

        //
        XPosition min;
        XPosition max;

        //
        for (int i = 0; i < positionsCount; i++)
        {
            //
            XPosition iPosition = positions[i];

            //
            if (iPosition.profit <= 0)
            {
                continue;
            }

            //
            // Calculate Min and Max ...

            //
            // Min ...
            if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
            {
                min = iPosition;
            }

            //
            // Max ...
            if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
            {
                max = iPosition;
            }

            //
            bool isSelectionMethodFilterPassed =
                //
                (method == NULL ||
                 method == X_POSITION_SELECT_ALL)
                    ? true
                    : false;
            //
            ;
            if (!isSelectionMethodFilterPassed)
            {
                continue;
            }

            //
            AddRef(
                iPosition,
                result
                //
            );
        }

        //
        if (min.IsValid() &&
            (method == X_POSITION_SELECT_MIN ||
             method == X_POSITION_SELECT_BOTH))
        {
            //
            AddRef(
                min,
                result
                //
            );
        }

        //
        if (max.IsValid() &&
            (method == X_POSITION_SELECT_MAX ||
             method == X_POSITION_SELECT_BOTH))
        {
            //
            AddRef(
                max,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Select In Drawdown Positions ...
    int GetInDrawdownPositions(
        XPosition &result[],                          // Hold's Result
        string symbol = NULL,                         // Trading Symbol
        string provider = NULL,                       // Signal Provider
        ENUM_TIMEFRAMES period = NULL,                // Trading Period
        ENUM_X_POSITION_TYPES type = NULL,            // Long (Buy) or Short (Sell)
        ENUM_X_POSITION_SELECT_METHODS method = NULL, // Selection Method
        bool filterByMagic = true,                    // Get Only Self Open Positions
        bool forceClean = true                        // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        XPosition positions[];
        int positionsCount = GetPositions(
            positions,
            symbol,
            provider,
            period,
            type,
            filterByMagic,
            forceClean);
        if (positionsCount <= 0)
        {
            return mResult;
        }

        //
        XPosition min;
        XPosition max;

        //
        for (int i = 0; i < positionsCount; i++)
        {
            //
            XPosition iPosition = positions[i];

            //
            if (iPosition.profit >= 0)
            {
                continue;
            }

            //
            // Calculate Min and Max ...

            //
            // Min ...
            if (!min.IsValid() || min.profit <= 0 || min.profit > iPosition.profit)
            {
                min = iPosition;
            }

            //
            // Max ...
            if (!max.IsValid() || max.profit <= 0 || max.profit < iPosition.profit)
            {
                max = iPosition;
            }

            //
            bool isSelectionMethodFilterPassed =
                //
                (method == NULL ||
                 method == X_POSITION_SELECT_ALL)
                    ? true
                    : false;
            //
            ;
            if (!isSelectionMethodFilterPassed)
            {
                continue;
            }

            //
            AddRef(
                iPosition,
                result
                //
            );
        }

        //
        if (min.IsValid() &&
            (method == X_POSITION_SELECT_MIN ||
             method == X_POSITION_SELECT_BOTH))
        {
            //
            AddRef(
                min,
                result
                //
            );
        }

        //
        if (max.IsValid() &&
            (method == X_POSITION_SELECT_MAX ||
             method == X_POSITION_SELECT_BOTH))
        {
            //
            AddRef(
                max,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Orders ...

    //
    // Retrieve Specific Order by Index ...
    bool GetOrder(
        int index,     // Specified Index
        XOrder &result // Hold's Result
    )
    {
        //
        return result
            .ByIndex(index);
    }

    //
    // Retrieve Specific Positions Orders ...
    int GetOrders(
        ulong ticket,          // Positions Ticket
        XOrder &result[],      // Hold's Result
        bool forceClean = true // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        // Select History by Specified Ticket ...
        bool isHistorySelected = HistorySelectByPosition(ticket);
        if (!isHistorySelected)
        {
            return mResult;
        }

        //
        int ordersCount = HistoryOrdersTotal();
        if (ordersCount <= 0)
        {
            return mResult;
        }

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        for (int i = 0; i < ordersCount; i++)
        {
            //
            XOrder iOrder;
            bool isLoaded = iOrder.HistoryByIndex(i);
            if (!isLoaded)
            {
                continue;
            }

            //
            bool isTicketFilterPassed =
                //
                iOrder.ticket = ticket
                //
                ;
            if (!isTicketFilterPassed)
            {
                continue;
            }

            //
            AddRef(
                iOrder,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Retrieve All Orders ...
    int GetOrders(
        XOrder &result[],                  // Hold's Result
        string symbol = NULL,              // Trading Symbol
        string provider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES period = NULL,     // Trading Period
        ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
        ENUM_ORDER_STATE state = NULL,     // Order State
        bool filterByMagic = true,         // Get Only Self Open Positions
        bool forceClean = true             // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        // Count Positions ...
        int itemsCount = OrdersTotal();
        if (itemsCount <= 0)
        {
            return mResult;
        }

        //
        // Loop Through all Positions ...
        for (int i = itemsCount - 1; i >= 0; i--)
        {
            //
            XOrder iItem;
            bool isLoaded = iItem.ByIndex(i);
            if (!isLoaded)
            {
                continue;
            }

            //
            // Now we have to Check Given Filters ...
            bool isFiltersPassed =
                iItem.IsFiltersPassed(
                    symbol,
                    provider,
                    period,
                    type,
                    state,
                    mMagicNumber,
                    filterByMagic
                    //
                );
            if (!isFiltersPassed)
            {
                continue;
            }

            //
            // Add Filtered Item into Result ...
            AddRef(
                iItem,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Retrieve Historical Orders ...
    int GetOrders(
        XOrder &result[],                  // Hold's Result
        string symbol = NULL,              // Trading Symbol
        string provider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES period = NULL,     // Trading Period
        ENUM_X_POSITION_TYPES type = NULL, // Long (Buy) or Short (Sell)
        ENUM_ORDER_STATE state = NULL,     // Order State
        datetime startDate = NULL,         // Specify Start Date
        datetime endDate = NULL,           // Specify End Date
        bool filterByMagic = true,         // Get Only Self Open Positions
        bool forceClean = true             // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        endDate = NormalizeTime(endDate);

        //
        if (!IsValid(startDate))
        {
            startDate = endDate - PeriodSeconds(PERIOD_D1);
        }

        //
        bool isHistorySelected = HistorySelect(
            startDate,
            endDate);
        if (!isHistorySelected)
        {
            return mResult;
        }

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        // Count Positions ...
        int itemsCount = OrdersTotal();
        if (itemsCount <= 0)
        {
            return mResult;
        }

        //
        // Loop Through all Positions ...
        for (int i = itemsCount - 1; i >= 0; i--)
        {
            //
            XOrder iItem;
            bool isLoaded = iItem.HistoryByIndex(i);
            if (!isLoaded)
            {
                continue;
            }

            //
            // Now we have to Check Given Filters ...
            bool isFiltersPassed =
                iItem.IsFiltersPassed(
                    symbol,
                    provider,
                    period,
                    type,
                    state,
                    mMagicNumber,
                    filterByMagic
                    //
                );
            if (!isFiltersPassed)
            {
                continue;
            }

            //
            // Add Filtered Item into Result ...
            AddRef(
                iItem,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Deals ...

    //
    // Retrieve Specific Deal by Index ...
    bool GetDeal(
        int index,    // Specified Index
        XDeal &result // Hold's Result
    )
    {
        //
        return result
            .ByIndex(index);
    }

    //
    bool GetLastDeal(
        XDeal &result // Hold's Result
    )
    {
        //
        bool mResult = false;

        //
        mResult = HistorySelect(0, TimeCurrent());
        if (!mResult)
        {
            return mResult;
        }

        //
        int lastDealIndex = HistoryDealsTotal() - 1;

        //
        mResult = GetDeal(
            lastDealIndex,
            result //
        );

        //
        return mResult;
    }

    //
    // Retrieve Specified Deal's History ...
    int GetDealsHistory(
        XDeal &deal,           // Specified Deal
        XOrder &history[],     // Deals History
        bool forceClean = true // Clean Result Array
    )
    {
        //
        int result = 0;

        //
        if (!deal.IsValid())
        {
            return result;
        }

        //
        result = GetOrders(
            deal.positionId,
            history,
            forceClean);

        //
        return result;
    }

    //
    // Retrieve All Deals ...
    int GetDeals(
        XDeal &result[],                // Hold's Result
        string symbol = NULL,           // Trading Symbol
        string provider = NULL,         // Signal Provider
        ENUM_TIMEFRAMES period = NULL,  // Trading Period
        ENUM_DEAL_TYPE type = NULL,     // Deal Type
        ENUM_DEAL_ENTRY entry = NULL,   // Deal Entry
        ENUM_DEAL_REASON reason = NULL, // Deal Reason
        datetime startDate = NULL,      // Specify Start Date
        datetime endDate = NULL,        // Specify End Date
        bool filterByMagic = true,      // Get Only Self Open Positions
        bool forceClean = true          // Clean Result Array
    )
    {
        //
        int mResult = 0;

        //
        endDate = NormalizeTime(endDate);

        //
        if (!IsValid(startDate))
        {
            startDate = endDate - PeriodSeconds(PERIOD_D1);
        }

        //
        bool isHistorySelected = HistorySelect(
            startDate,
            endDate);
        if (!isHistorySelected)
        {
            return mResult;
        }

        //
        int before = ArraySize(result);

        //
        if (forceClean)
        {
            Clean(result);
        }

        //
        // Count Positions ...
        int itemsCount = HistoryDealsTotal();
        if (itemsCount <= 0)
        {
            return mResult;
        }

        //
        // Loop Through all Positions ...
        for (int i = itemsCount - 1; i >= 0; i--)
        {
            //
            XDeal iItem;
            bool isLoaded = iItem.ByIndex(i);
            if (!isLoaded)
            {
                continue;
            }

            //
            // Now we have to Check Given Filters ...
            bool isFiltersPassed =
                iItem.IsFiltersPassed(
                    symbol,
                    provider,
                    period,
                    type,
                    entry,
                    reason,
                    mMagicNumber,
                    filterByMagic
                    //
                );
            if (!isFiltersPassed)
            {
                continue;
            }

            //
            // Add Filtered Item into Result ...
            AddRef(
                iItem,
                result
                //
            );
        }

        //
        int after = ArraySize(result);

        //
        mResult = after - before;

        //
        return mResult;
    }

    //
    // Support Functions ...

    //
    // Check a Position has Support or not ...
    bool HasSupport(
        ulong ticket,
        bool forceOpen = true //
    )
    {
        //
        bool result = false;

        //
        result = ticket > 0;
        if (!result)
        {
            return result;
        }

        //
        if (forceOpen)
        {
            //
            result = IsOpen(ticket);
            if (!result)
            {
                return result;
            }
        }

        //
        XPosition positions[];
        int positionsCount = GetPositions(positions);
        result = IsValidSize(positionsCount);
        if (!result)
        {
            return result;
        }

        //
        // Extract Support Positions ...
        XPosition supports[];
        int supportsCount = ExtractSupports(
            positions,
            supports //
        );
        result = IsValidSize(supportsCount);
        if (!result)
        {
            return result;
        }

        //
        // Loop Through Supports ...
        for (int i = 0; i < supportsCount; i++)
        {
            //
            XPosition iSupport = supports[i];

            //
            ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
            ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);

            //
            result =
                //
                (supportedTicket > 0 && ticket == supportedTicket)
                //
                ||
                //
                (eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
                //
                ;
            if (result)
            {
                break;
            }
        }

        //
        return result;
    }

    //
    // Retrieve Specific Position's Supports ...
    int GetSupports(
        ulong ticket,
        XPosition &dest[],    // Result ...
        bool forceOpen = true //
    )
    {
        //
        int result = 0;

        //
        Clean(dest);

        //
        bool hasSupport = HasSupport(
            ticket,
            forceOpen //
        );
        if (!hasSupport)
        {
            return result;
        }

        //
        XPosition positions[];
        int positionsCount = GetPositions(positions);
        if (!IsValidSize(positionsCount))
        {
            return result;
        }

        //
        // Extract Support Positions ...
        XPosition supports[];
        int supportsCount = ExtractSupports(
            positions,
            supports //
        );
        if (!IsValidSize(supportsCount))
        {
            return result;
        }

        //
        // Loop Through Supports ...
        for (int i = 0; i < supportsCount; i++)
        {
            //
            XPosition iSupport = supports[i];

            //
            ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
            ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);

            //
            bool isOwn =
                //
                (supportedTicket > 0 && ticket == supportedTicket)
                //
                ||
                //
                (eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
                //
                ;
            if (isOwn)
            {
                //
                AddRef(
                    iSupport,
                    dest //
                );
            }
        }

        //
        result = ArraySize(dest);

        //
        return result;
    }

    //
    // Retrieve Specific Position's Supports ...
    int GetSupports(
        ulong ticket,
        XOrder &dest[],       // Result ...
        bool forceOpen = true //
    )
    {
        //
        int result = 0;

        //
        Clean(dest);

        //
        bool hasSupport = HasSupport(
            ticket,
            forceOpen //
        );
        if (!hasSupport)
        {
            return result;
        }

        //
        XOrder orders[];
        int ordersCount = GetOrders(
            orders,
            NULL,                // All Symbols ...
            NULL,                // All Providers ...
            NULL,                // All Periods ...
            X_POSITION_TYPE_ALL, // All Types ...
            ORDER_STATE_PLACED,  // Untriggere Orders ...
            true                 //
        );
        if (!IsValidSize(ordersCount))
        {
            return result;
        }

        //
        // Extract Support Positions ...
        XOrder supports[];
        int supportsCount = ExtractSupports(
            orders,
            supports //
        );
        if (!IsValidSize(supportsCount))
        {
            return result;
        }

        //
        // Loop Through Supports ...
        for (int i = 0; i < supportsCount; i++)
        {
            //
            XOrder iSupport = supports[i];

            //
            ulong supportedTicket = ExtractSupportedTicket(iSupport.comment);
            ulong eqmSupportedTicket = ExtractEQMSupportedTicket(iSupport.comment);

            //
            bool isOwn =
                //
                (supportedTicket > 0 && ticket == supportedTicket)
                //
                ||
                //
                (eqmSupportedTicket > 0 && ticket == eqmSupportedTicket)
                //
                ;
            if (isOwn)
            {
                //
                AddRef(
                    iSupport,
                    dest //
                );
            }
        }

        //
        result = ArraySize(dest);

        //
        return result;
    }

    //
    // Tools ...

    //
    // Retrieve Last Open Position Ticket ...
    ulong GetLastOpenPositionTicket()
    {
        //
        ulong result = 0;

        int positionsCount = PositionsTotal();
        if (positionsCount <= 0)
        {
            return result;
        }

        //
        result = PositionGetTicket(PositionsTotal() - 1);

        //
        return result;
    }

    //
    // Retrieve Last Placed Order Ticket ...
    ulong GetLastPlacedOrderTicket()
    {
        //
        ulong result = 0;

        int ordersCount = OrdersTotal();
        if (ordersCount <= 0)
        {
            return result;
        }

        //
        if (mOrderInfo.SelectByIndex(ordersCount - 1))
        {
            result = mOrderInfo.Ticket();
        }

        //
        return result;
    }

    //
    // Prepare Dynamic Volume ...
    double GetDynamicVolume(
        string _symbol,
        double _balanceFactor,
        double _volumeStep //
    )
    {
        //
        double result = 0;

        //
        // Validate Inputs ...
        if (!IsValid(_symbol) ||
            _volumeStep <= 0 ||
            _balanceFactor <= 0)
        {
            return result;
        }

        //
        // Retrieve Account Balance ...
        double balance = mAccount.GetBalance();

        //
        double bStep = balance / _balanceFactor;
        bStep = MathFloor(bStep);
        if (bStep == 0)
        {
            bStep = 1;
        }

        //
        result = bStep * _volumeStep;

        //
        result = NormalizeVolume(
            result,
            _symbol //
        );

        //
        return result;
    }

    //
    // Protected ...
  protected:
    //
    // Props ...

    //
    // Event Listener Notifiers ...

    //
    // State ...
    void NotifyTradeStateChangeEvent(const XOnTradeHandlerState &state)
    {
        //
        int count = ArraySize(mTradeStateChangedEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnTradeStateChanged iHandler = mTradeStateChangedEventHandlers[i];
            iHandler(state);
        }
    }

    //
    // Deals ...
    void NotifyDealsChangeEvent(int changes)
    {
        //
        int count = ArraySize(mDealsChangedEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnDealsChanged iHandler = mDealsChangedEventHandlers[i];
            iHandler(changes);
        }
    }

    //
    // Orders ...
    void NotifyOrdersChangeEvent(int changes)
    {
        //
        int count = ArraySize(mOrdersChangedEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnOrdersChanged iHandler = mOrdersChangedEventHandlers[i];
            iHandler(changes);
        }
    }

    //
    // Positions ...
    void NotifyPositionsChangeEvent(int changes)
    {
        //
        int count = ArraySize(mPositionsChangedEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnPositionsChanged iHandler = mPositionsChangedEventHandlers[i];
            iHandler(changes);
        }
    }

    //
    // StopLoss ...
    void NotifyStopLossEvent(const XDeal &deal)
    {
        //
        int count = ArraySize(mStopLossEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnStopLoss iHandler = mStopLossEventHandlers[i];
            iHandler(deal);
        }
    }

    //
    // Take Profit ...
    void NotifyTakeProfitEvent(const XDeal &deal)
    {
        //
        int count = ArraySize(mTakeProfitEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnTakeProfit iHandler = mTakeProfitEventHandlers[i];
            iHandler(deal);
        }
    }

    //
    void NotifyModifyEvent(
        ulong ticket,
        double profit,
        string comment //
    )
    {
        //
        int count = ArraySize(mOnModifyEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnModify iHandler = mOnModifyEventHandlers[i];
            iHandler(
                ticket,
                profit,
                comment //
            );
        }
    }

    //
    void NotifyForceCloseEvent(
        ulong ticket,
        XPosition &position,
        string comment //
    )
    {
        //
        int count = ArraySize(mOnForceCloseEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnForceClose iHandler = mOnForceCloseEventHandlers[i];
            iHandler(
                ticket,
                position,
                comment //
            );
        }
    }

    //
    void NotifyPartialCloseEvent(
        ulong ticket,
        double profit,
        string comment //
    )
    {
        //
        int count = ArraySize(mOnPartialCloseEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnModify iHandler = mOnPartialCloseEventHandlers[i];
            iHandler(
                ticket,
                profit,
                comment //
            );
        }
    }

    //
    void NotifySignalExecuted(XSignal &signal)
    {
        //
        bool has = signal.IsValid() &&
                   signal.IsExecuted();
        if (!has)
        {
            return;
        }

        //
        int count = ArraySize(mOnSignalExecutedEventHandlers);
        if (count <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            TOnSignal iHandler = mOnSignalExecutedEventHandlers[i];
            iHandler(signal);
        }
    }

    //
    // Tools ...

    //
    // Retrieve Specified Position Comment ...
    string GetPositionComment(ulong ticket)
    {
        //
        string result = NULL;

        //
        if (!IsOpen(ticket))
        {
            return result;
        }

        //
        XPosition position;
        bool isLoaded = position.ByTicket(ticket);
        if (!isLoaded)
        {
            return result;
        }

        //
        result = position.comment;

        //
        return result;
    }

    //
    ENUM_TIMEFRAMES GetDealPeriod(XDeal &deal)
    {
        //
        ENUM_TIMEFRAMES result = NULL;

        //
        XOrder orders[];
        int ordersCount = GetDealsHistory(
            deal,
            orders);
        if (!ordersCount)
        {
            return result;
        }

        //
        for (int i = 0; i < ordersCount; i++)
        {
            //
            XOrder iOrder = orders[i];

            //
            if (IsValid(iOrder.comment))
            {
                //
                ENUM_TIMEFRAMES iPeriod = ExtractPeriod(iOrder.comment);
                if (IsValid(iPeriod))
                {
                    result = iPeriod;
                    break;
                }
            }
        }

        //
        return result;
    }

    //
    double GetMaxAllowedEquity()
    {
        //
        double equity = mAccount.GetEquity();
        double balance = mAccount.GetBalance();

        //
        double balanceForFactor = MathAbs(equity - balance);
        if (balanceForFactor <= 0)
        {
            balanceForFactor = balance;
        }
        else
        {
            balanceForFactor = equity;
        }

        //
        double result = balanceForFactor * mMaxAllowedDrawdownFactor;

        //
        return result;
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    int mSlippage;      // Slippage ...
    ulong mMagicNumber; // Magic Number (Unique Identifier) ...

    //
    double mMaxAllowedSpread;         // Max Allowed Spred for Opening Trades
    int mMaxAllowedPositions;         // Max Allowed Same Positions
    double mMaxAllowedDrawdownFactor; // Max Allowed Drawdown Factor

    //
    XCTradeBase *mTrader; // Base CTrade Manipulated Instance ...

    //
    CDealInfo mDealInfo;                 // Deals Info ...
    COrderInfo mOrderInfo;               // Order Info ...
    CPositionInfo mPositionInfo;         // Positions Info
    CHistoryOrderInfo mHistoryOrderInfo; // History Orders Info ...

    //
    // Position Info Collector ...

    //
    // Tools ...

    //
    // OnTrade Event Handlers Section ...

    //
    // Props ...

    //
    int mDaysForRead;        // Reading Dates for Trade History Checker ...
    datetime mEndDate;       // End Date for Trade history Checking ...
    datetime mStartDate;     // Start Date for Trade history Checking ... ...
    int mOrdersCount;        // Number of Active orders ...
    int mPositionsCount;     // Number of Open positions ...
    int mDealsCount;         // Number of Deals in the Trade History Checking ...
    int mHistoryOrdersCount; // Number of Orders in the Trade History Checking ...
    bool mScanStarted;       // a Flag of counter relevance ...

    //
    // Event Listeners ...

    //
    // Deals Changed Event Handlers ...
    TOnDealsChanged mDealsChangedEventHandlers[];

    //
    // Orders Changed Event Handlers ...
    TOnOrdersChanged mOrdersChangedEventHandlers[];

    //
    // Positions Changed Event Handlers ...
    TOnPositionsChanged mPositionsChangedEventHandlers[];

    //
    // Trade State Changed Event Handlers ...
    TOnTradeStateChanged mTradeStateChangedEventHandlers[];

    //
    // Signal Executed Event Handlers ...
    TOnSignal mOnSignalExecutedEventHandlers[];

    //
    TOnModify mOnModifyEventHandlers[];
    TOnForceClose mOnForceCloseEventHandlers[];
    TOnPartialClose mOnPartialCloseEventHandlers[];

    //
    // TPSL ...
    TOnStopLoss mStopLossEventHandlers[];
    TOnTakeProfit mTakeProfitEventHandlers[];

    //
    // Reset On Trade Scanner Context ...
    void ResetOnTradeContext()
    {
        //
        mEndDate = TimeCurrent();
        mStartDate = mEndDate - (mDaysForRead * PeriodSeconds(PERIOD_D1));
    }

    //
    // Initial Context Of Trade Scanner ...
    void InitOnTradeContext()
    {
        //
        ResetLastError();

        //
        mOrdersCount = OrdersTotal();
        mPositionsCount = PositionsTotal();

        //
        // load history ...
        bool selected = HistorySelect(mStartDate, mEndDate);
        if (!selected)
        {
            return;
        }

        //
        // get the current value ...
        mDealsCount = HistoryDealsTotal();
        mHistoryOrdersCount = HistoryOrdersTotal();

        //
        mScanStarted = true;
    }

    //
    // Check Statrt Date In Trade History ...
    void CheckStartDateInTradeHistory()
    {
        //
        // initial interval, if we were to start working right now ..
        datetime currStart = TimeCurrent() - (mDaysForRead * PeriodSeconds(PERIOD_D1));

        //
        // make sure that the start limit of the trade history has not gone
        // more than 1 day over the intended date ...
        if (currStart - mStartDate > PeriodSeconds(PERIOD_D1))
        {
            //
            // correct the start date of history to be loaded in the cache ...
            mStartDate = currStart;

            //
            // now reload the trade history for the updated interval ...
            HistorySelect(mStartDate, mEndDate);

            //
            // correct the deal and order counters in history for further comparison
            mHistoryOrdersCount = HistoryOrdersTotal();
            mDealsCount = HistoryDealsTotal();
        }
    }

    //
    // Process On Trade Context ...
    void ProcessOnTradeContext(
        XOnTradeHandlerState &result // Hold's Result
    )
    {
        //
        mEndDate = TimeCurrent();

        //
        ResetLastError();

        //
        // download trading history from the specified interval to the program cache ...
        bool selected = HistorySelect(mStartDate, mEndDate);
        if (!selected)
        {
            return;
        }

        //
        // get the current values ...
        int currOrders = OrdersTotal();
        int currPositions = PositionsTotal();
        int currDeals = HistoryDealsTotal();
        int currHistoryOrders = HistoryOrdersTotal();

        //
        // Orders ...
        // check if the number of active orders has been changed ...
        if (currOrders != mOrdersCount)
        {
            //
            // number of active orders has been changed ...
            result.hasNewOrder = true;
            result.newOrders = currOrders - mOrdersCount;

            //
            // update the value ...
            mOrdersCount = currOrders;
        }

        //
        // Positions ...
        // changes in the number of open positions ...
        if (currPositions != mPositionsCount)
        {
            //
            // number of open positions has been changed ...
            result.hasNewPosition = true;
            result.newPositions = currPositions - mPositionsCount;

            //
            // update the value ...
            mPositionsCount = currPositions;
        }

        //
        // Deals ...
        // changes in the number of deals in the trade history cache ...
        if (currDeals != mDealsCount)
        {
            //
            // number of deals in the trade history cache has been changed ...
            result.hasNewDeal = true;
            result.newDeals = currDeals - mDealsCount;

            //
            // update the value ...
            mDealsCount = currDeals;
        }

        //
        // History Orders ...
        // changes in the number of history orders in the trade history cache ...
        if (currHistoryOrders != mHistoryOrdersCount)
        {
            //
            // number of history orders in the trade history cache has been changed ...
            result.hasNewHistoryOrder = true;
            result.newHistoryOrders = currHistoryOrders - mHistoryOrdersCount;

            //
            // update the value ...
            mHistoryOrdersCount = currHistoryOrders;
        }

        //
        // checking if it is necessary to change the limits of the trade history to be requested in cache ...
        CheckStartDateInTradeHistory();
    }
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Classes\x-saherelm.x-volume.class.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCVolume
// Description: provides all Requirements for Volume Managing ...
//
//
// 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

//
// Template Variables of Inputs for Using ...
// input group "Volume";
// input ENUM_X_VOLUME_SELECT_TYPE eaVolumeSelect = X_VOLUME_STATIC; // Volume Applying Type
// input double eaStaticVoluem = X_MIN_VOLUME;                       // Static Volume
// input double eaDynamicVolumeStepBalance = 0.0;                    // Step of Balance for Increase Volume
// input double eaDynamicVolumeStepVolume = X_MIN_VOLUME;            // Step of Volume Increasing
// input double eaConstantRiskBalance = 0.0;                         // Constant Risk Balance per Trade
// input double eaConstantPercent = 0.0;                             // Constant Percent of Balance Per Trade
// input double eaConstantBalance = 0.0;                             // Constant Balance for Calculations
// XCVolume *eaVolume;
// eaVolume = new XCVolume();
// bool result = eaVolume.Init(
//     eaVolumeSelect,
//     eaStaticVoluem,
//     eaDynamicVolumeStepBalance,
//     eaDynamicVolumeStepVolume,
//     eaConstantRiskBalance,
//     eaConstantPercent,
//     eaConstantBalance //
// );
// if (!result) {
//     return INIT_FAILED;
// }

//
// Imports ...
#include "../Classes/x-saherelm.x-account.class.mq5"
#include "../Libraries/x-saherelm.x-trade.lib.mq5"

//
// Definitions ...
#define X_MIN_VOLUME 0.01

//
enum ENUM_X_VOLUME_SELECT_TYPE
{
    X_VOLUME_NONE,    // None
    X_VOLUME_STATIC,  // Static Volume
    X_VOLUME_CURRENT, // Current Balance
    X_VOLUME_EQUITY,  // Current Equity
    X_VOLUME_CONSTANT // Constant Value
};

//
bool IsValid(ENUM_X_VOLUME_SELECT_TYPE value)
{
    //
    bool result = false;

    //
    result = value != X_VOLUME_NONE;

    //
    return result;
}

//
string ToString(ENUM_X_VOLUME_SELECT_TYPE value)
{
    //
    string result = NULL;

    //
    result = EnumToString(value);

    //
    return result;
}

//
// Implementation ...
class XCVolume : public XCBase
{
    //
    // Public ...
  public:
    //
    // Props ...
    XCAccount *account;

    //
    // Constructor ...
    XCVolume()
    {
        //
        account = new XCAccount();

        //
        Default();
    }

    //
    // Deconstructor ...
    ~XCVolume()
    {
        //
        ZeroMemory(account);
    }

    //
    bool Init(
        ENUM_X_VOLUME_SELECT_TYPE _SelectType,
        double _StaticVolume,
        double _DynamicVolumeStepBalance,
        double _DynamicVolumeStepVolume,
        double _ConstantRiskBalance,
        double _ConstantPercent,
        double _ConstantBalance //
    )
    {
        //
        bool result = false;

        //
        mVolumeType = _SelectType;
        mStaticVolume = _StaticVolume;
        mConstantPercent = _ConstantPercent;
        mConstantBalance = _ConstantBalance;
        mConstantRiskBalance = _ConstantRiskBalance;
        mDynamicVolumeStepVolume = _DynamicVolumeStepVolume;
        mDynamicVolumeStepBalance = _DynamicVolumeStepBalance;

        //
        result =
            //
            CanUseStaticVolume() ||
            CanUseDynamicVolume() ||
            CanUseConstantRiskBalance() ||
            CanUseConstantBalancePercent()
            //
            ;

        //
        return result;
    }

    //
    // Prperties ...

    /**
     * Get Volume Calculating Method ...
     *
     * @return (ENUM_X_VOLUME_SELECT_TYPE)
     */
    ENUM_X_VOLUME_SELECT_TYPE VolumeType()
    {
        return mVolumeType;
    }

    /**
     * Set Volume Calculating Method ...
     *
     * @param  value: ENUM_X_VOLUME_SELECT_TYPE member ...
     */
    void VolumeType(ENUM_X_VOLUME_SELECT_TYPE value)
    {
        mVolumeType = value;
    }

    /**
     * Get Static Volume ...
     *
     * @return ( double )
     */
    double StaticVolume()
    {
        return mStaticVolume;
    }

    /**
     * Set Static Volume ...
     *
     * @param  value: double
     */
    void StaticVolume(double value)
    {
        //
        value = NormalizeDouble(value, 0.01, 1);

        //
        mStaticVolume = value;
    }

    /**
     * Get Dynamic Volume Step Balance ...
     * used to Calculate Final Lottage ...
     *
     * @return ( double )
     */
    double DynamicVolumeStepBalance()
    {
        return mDynamicVolumeStepBalance;
    }

    /**
     * Set Dynamic Volume Step Balance ...
     * used to Calculate Final Lottage ...
     *
     * @param  value: Argument 1
     */
    void DynamicVolumeStepBalance(double value)
    {
        //
        value = NormalizeDouble(value, 0);

        //
        mDynamicVolumeStepBalance = value;
    }

    /**
     * Get Dynamic Volume Step Volume ...
     * used to Calculate Final Lottage ...
     * how much increase Final Volume based oBalance Step ...
     *
     * @return ( double )
     */
    double DynamicVolumeStepVolume()
    {
        return mDynamicVolumeStepVolume;
    }

    /**
     * Get Dynamic Volume Step Volume ...
     * used to Calculate Final Lottage ...
     * how much increase Final Volume based oBalance Step ...
     *
     * @param  value: double
     */
    void DynamicVolumeStepVolume(double value)
    {
        //
        value = NormalizeDouble(value, 0.01, 0.1);

        //
        mDynamicVolumeStepVolume = value;
    }

    /**
     * Get Constant Balance Use for Dyamic Volume Calculation ...
     *
     * @return ( double )
     */
    double ConstantBalance()
    {
        return mConstantBalance;
    }

    /**
     * Set Constant Balance Use for Dyamic Volume Calculation ...
     *
     * @param  value: Argument 1
     */
    void ConstantBalance(double value)
    {
        //
        value = NormalizeDouble(value, 0);

        //
        mConstantBalance = value;
    }

    /**
     * Get Constant Risk Value Based on Account Currency ...
     *
     * @return ( double )
     */
    double ConstantRiskBalance()
    {
        return mConstantRiskBalance;
    }

    /**
     * Get Constant Risk Value Based on Account Currency ...
     *
     * @param  value: double
     */
    void ConstantRiskBalance(double value)
    {
        //
        value = NormalizeDouble(value, 0);

        //
        mConstantRiskBalance = value;
    }

    /**
     * Get Constant Percent Based On Selected Balance ...
     *
     * @return ( double )
     */
    double ConstantPercent()
    {
        return mConstantPercent;
    }

    /**
     * Set Constant Percent Based On Selected Balance ...
     *
     * @param  value: double ...
     */
    void ConstantPercent(double value)
    {
        //
        value = NormalizeDouble(value, 0, 100);

        //
        mConstantPercent = value;
    }

    //
    // Tools ...

    /**
     * Retrieve Balance based on Provided Volume Type ...
     *
     * @return ( double )
     */
    double GetBalance()
    {
        //
        double result = 0;

        //
        if (!IsValid(mVolumeType))
        {
            return result;
        }

        //
        if (mConstantBalance > 0 &&
            mVolumeType == X_VOLUME_CONSTANT)
        {
            result = mConstantBalance;
        }
        else if (mVolumeType == X_VOLUME_CURRENT)
        {
            result = account.GetBalance();
        }
        else if (mVolumeType == X_VOLUME_EQUITY)
        {
            result = account.GetEquity();
        }

        //
        return result;
    }

    /**
     * Retrieve Balance based on Provided Volume Type ...
     *
     * @return ( double )
     */
    double GetBalance(ENUM_X_VOLUME_SELECT_TYPE type)
    {
        //
        double result = 0;

        //
        if (!IsValid(type))
        {
            return result;
        }

        //
        if (mConstantBalance > 0 &&
            type == X_VOLUME_CONSTANT)
        {
            result = mConstantBalance;
        }
        else if (type == X_VOLUME_CURRENT)
        {
            result = account.GetBalance();
        }
        else if (type == X_VOLUME_EQUITY)
        {
            result = account.GetEquity();
        }

        //
        return result;
    }

    /**
     * Calculate Volume Based On Given Configuration ...
     *
     * @param  symbol: string, Trading Symbol ...
     * @param  entry: double, Entry Price ...
     * @param  sl: double, Stop Loss Price ...
     *
     * @return ( double )
     */
    double CalculateVolume(
        string symbol,
        double entry,
        double sl //
    )
    {
        //
        double result = X_MIN_VOLUME;

        //
        bool has = false;

        //
        // Validate Args ...
        has =
            sl > 0 &&
            entry > 0 &&
            IsValid(symbol);
        if (!has)
        {
            return result;
        }

        //
        // Retrieve Balance Type ...
        double balance = GetBalance();
        double points = GetPoints(symbol);
        double riskPrice = MathAbs(entry - sl);
        double riskInPoints = riskPrice / points;

        //
        has = CanUseStaticVolume();
        if (has)
        {
            result = mStaticVolume;
        }

        //
        has = CanUseDynamicVolume();
        if (has)
        {
            //
            double multiplier = balance / mDynamicVolumeStepBalance;
            if (multiplier < 0)
            {
                multiplier = 1;
            }

            //
            result = multiplier * mDynamicVolumeStepVolume;
        }

        //
        has = CanUseConstantRiskBalance();
        if (has)
        {
            //
            result = account.CalculateVolume(
                symbol,
                mConstantRiskBalance,
                riskInPoints //
            );
        }

        //
        has = CanUseConstantBalancePercent();
        if (has)
        {
            //
            double riskAmountPerBalance = (mConstantPercent * balance) / 100;

            //
            result = account.CalculateVolume(
                symbol,
                riskAmountPerBalance,
                riskInPoints //
            );
        }

        //
        if (result < X_MIN_VOLUME)
        {
            result = X_MIN_VOLUME;
        }

        //
        // Normalize Volume ...
        result = NormalizeVolume(result, symbol);

        //
        return result;
    }

    /**
     * Calculate Volume Based On Given Configuration ...
     *
     * @param  signal: XSignal instance reference, provided required Data ...
     *
     * @return ( double )
     */
    double CalculateVolume(XSignal &signal)
    {
        //
        double result = X_MIN_VOLUME;

        //
        if (!signal.IsValid())
        {
            return result;
        }

        //
        result = CalculateVolume(
            signal.symbol,
            signal.entry,
            signal.sl //
        );

        //
        return result;
    }

    /**
     * Calculate Specified Percent of Selected Balance for Volume ...
     *
     * @param  percent: double, percent of Balance Risking ...
     * @param  symbol: string, Trading Symbol ...
     * @param  entry: double, Entry Price ...
     * @param  sl: double Stop Loss Price ...
     *
     * @return ( double )
     */
    double CalculateVolume(
        ENUM_X_VOLUME_SELECT_TYPE selectType,
        double percent,
        string symbol,
        double entry,
        double sl //
    )
    {
        //
        double result = X_MIN_VOLUME;

        //
        bool has = false;

        //
        // Validate Args ...
        has =
            sl > 0 &&
            entry > 0 &&
            percent > 0 &&
            IsValid(symbol) &&
            IsValid(selectType) &&
            (selectType == X_VOLUME_EQUITY ||
             selectType == X_VOLUME_CURRENT);
        if (!has)
        {
            return result;
        }

        //
        // Retrieve Balance Type ...
        double points = GetPoints(symbol);
        double riskPrice = MathAbs(entry - sl);
        double balance = GetBalance(selectType);
        double riskInPoints = riskPrice / points;

        //
        double riskAmountPerBalance = (percent * balance) / 100;

        //
        result = account.CalculateVolume(
            symbol,
            riskAmountPerBalance,
            riskInPoints //
        );

        //
        return result;
    }

    //
    // Protected ...
  protected:
    //
    // Tools ...

    /**
     * Set Default Props ...
     */
    void Default()
    {
        //
        VolumeType(X_VOLUME_STATIC);
        StaticVolume(0.01);

        //
        ConstantBalance(0);
        ConstantPercent(0);
        ConstantRiskBalance(0);

        //
        DynamicVolumeStepBalance(0);
        DynamicVolumeStepVolume(0.01);
    }

    /**
     * Check Configuration is Valid For Static Volume ...
     *
     * @return ( bool )
     */
    bool CanUseStaticVolume()
    {
        //
        bool result = false;

        //
        result = IsValid(mVolumeType);
        if (!result)
        {
            return result;
        }

        //
        // Static Volume ...
        result =
            //
            mVolumeType == X_VOLUME_STATIC &&
            mStaticVolume > 0 &&
            //
            mDynamicVolumeStepBalance == 0 &&
            //
            mConstantBalance == 0 &&
            mConstantPercent == 0 &&
            mConstantRiskBalance == 0
            //
            ;

        //
        return result;
    }

    /**
     * Check Configuration is Valid For Dynamic Volume ...
     *
     * @return ( bool )
     */
    bool CanUseDynamicVolume()
    {
        //
        bool result = false;

        //
        result = IsValid(mVolumeType);
        if (!result)
        {
            return result;
        }

        //
        result =
            //
            (mVolumeType == X_VOLUME_CURRENT ||
             mVolumeType == X_VOLUME_EQUITY ||
             (mVolumeType == X_VOLUME_CONSTANT &&
              mConstantBalance > 0)) &&
            //
            mDynamicVolumeStepBalance > 0 &&
            mDynamicVolumeStepVolume > 0 &&
            //
            mStaticVolume == 0 &&
            mConstantPercent == 0 &&
            mConstantRiskBalance == 0
            //
            ;

        //
        return result;
    }

    /**
     * Check Configuration is Valid For Constant Risk Balance ...
     *
     * @return ( bool )
     */
    bool CanUseConstantRiskBalance()
    {
        //
        bool result = false;

        //
        result = IsValid(mVolumeType);
        if (!result)
        {
            return result;
        }

        //
        result =
            //
            (mVolumeType == X_VOLUME_CURRENT ||
             mVolumeType == X_VOLUME_EQUITY ||
             (mVolumeType == X_VOLUME_CONSTANT &&
              mConstantBalance > 0)) &&
            //
            mConstantRiskBalance > 0 &&
            //
            mStaticVolume == 0 &&
            mConstantPercent == 0 &&
            mDynamicVolumeStepBalance == 0
            //
            ;

        //
        return result;
    }

    /**
     * Check Configuration is Valid For Constant Risk Percent ...
     *
     * @return ( bool )
     */
    bool CanUseConstantBalancePercent()
    {
        //
        bool result = false;

        //
        result = IsValid(mVolumeType);
        if (!result)
        {
            return result;
        }

        //
        result =
            //
            (mVolumeType == X_VOLUME_CURRENT ||
             mVolumeType == X_VOLUME_EQUITY ||
             (mVolumeType == X_VOLUME_CONSTANT &&
              mConstantBalance > 0)) &&
            //
            mConstantPercent > 0 &&
            //
            mStaticVolume == 0 &&
            mConstantRiskBalance == 0 &&
            mDynamicVolumeStepBalance == 0
            //
            ;

        //
        return result;
    }

    //
    // Private ...
  private:
    //
    // Props ...

    //
    ENUM_X_VOLUME_SELECT_TYPE mVolumeType; // Balance Type ...

    //
    // Static Volume ...
    double mStaticVolume; // Static Volume ...

    //
    double mDynamicVolumeStepBalance; // Step of Balance ...
    double mDynamicVolumeStepVolume;  // Step of Volume ...

    //
    double mConstantBalance;     // Constant Balance ....
    double mConstantRiskBalance; // Constant Risk Balance ...
    double mConstantPercent;     // Constant Percent of Balance ...
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Documents\Helpers\x-saherelm.x121.xcc.helper.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Helper Class Library
// ----------------------------------------------
// Name: XSCX121XCCHelper
// Description: provides all Indicator
// Helper requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-helper.class.mq5"

//
// Definitions ...

//
// Input Models ...
struct X121XCCInputs
{
    //
    // Props ...

    //
    // Chart Style ...
    ENUM_CHART_MODE mode; // Mode
    color upColor;        // Up Color
    color downColor;      // Down Color
    color lineColor;      // Line mode and Doji candlestick Color
    color bearishColor;   // Bullish Color
    color bullishColor;   // Bearish Color
    color volumesColor;   // Volumes Color

    //
    // Presentation ...
    bool showCandles; // Show Candles

    //
    // Constructor(s) ...
    X121XCCInputs()
    {
        //
        Clean();
    }

    //
    // Tools ...

    //
    // Clean ...
    void Clean()
    {
        //
        mode = CHART_CANDLES;

        //
        upColor = CLR_NONE;
        downColor = CLR_NONE;
        lineColor = CLR_NONE;
        bearishColor = CLR_NONE;
        bullishColor = CLR_NONE;
        volumesColor = CLR_NONE;

        //
        showCandles = false;

        //
        ZeroMemory(this);
    }

    //
    // Default ...
    void Default()
    {
        //
        mode = CHART_CANDLES;

        //
        upColor = clrLime;
        downColor = clrRed;
        lineColor = clrLime;
        bearishColor = clrRed;
        bullishColor = clrLime;
        volumesColor = clrGreen;
        //
        showCandles = true;
    }

    //
    // Hide all Visible Buffers ...
    void Hide()
    {
        showCandles = false;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            true
            //
            ;

        //
        return result;
    }

    //
    // Retrieve MAx Length ...
    int Max()
    {
        //
        int result = 0;

        //
        result = MathMax(0, 0);

        //
        return result;
    }
};

//
// Class ...
class XCX121XCCHelper : public XCBaseHelper
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructors ...
    XCX121XCCHelper()
        : XCBaseHelper(_Symbol, _Period)
    {
    }

    //
    // Deconstructor ...
    ~XCX121XCCHelper()
    {
        mInputs.Clean();
    }

    //
    // Tools ...
    bool Init(
        string symbol,          // Trading Symbol
        ENUM_TIMEFRAMES period, // Trading Period
        X121XCCInputs &inputs   // Inputs
    )
    {
        //
        bool result = false;

        //
        mSymbol = symbol;
        mPeriod = period;

        //
        result = inputs.IsValid();
        if (!result)
        {
            return result;
        }

        //
        mInputs = inputs;

        //
        mHandler = iCustom(
            mSymbol,
            mPeriod,
            "x-saherelm.x121.xcc",
            //
            // Inputs ...
            //
            // Chart Style ...
            "",
            mInputs.mode,         // Mode
            mInputs.upColor,      // Up Color
            mInputs.downColor,    // Down Color
            mInputs.lineColor,    // Line mode and Doji candlestick Color
            mInputs.bullishColor, // Bearish Color
            mInputs.bearishColor, // Bullish Color
            mInputs.volumesColor, // Volumes Color
            //
            // Presentation ...
            "",
            mInputs.showCandles // Show Candles
            //
        );
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // Inputs ...

    //
    X121XCCInputs GetInputs()
    {
        return mInputs;
    }

    //
    bool SetInputs(
        X121XCCInputs &inputs // Configs
    )
    {
        //
        return Init(
            mSymbol,
            mPeriod,
            inputs
            //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...
    X121XCCInputs mInputs; // Inputs ...
};

//
// Tools ...

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Documents\Helpers\x-saherelm.x121.xct.helper.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Helper Class Library
// ----------------------------------------------
// Name: XSCX121XCTHelper
// Description: provides all Indicator
// Helper requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-helper.class.mq5"

//
// Definitions ...

//
// Input Models ...
struct X121XCTInputs
{
    //
    // Props ...

    //
    string font;             // Font
    int fontSize;            // Font Size;
    color clr;               // Text Color
    ENUM_BASE_CORNER corner; // Text Position

    //
    // Presentation ...
    bool showCandleTime; // Show Candle Time

    //
    // Constructor(s) ...
    X121XCTInputs()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Clean ...
    void Clean()
    {
        //
        font = NULL;
        fontSize = 15;
        clr = CLR_NONE;
        corner = CORNER_RIGHT_LOWER;

        //
        // Presentation ...
        showCandleTime = false;

        //
        ZeroMemory(this);
    }

    //
    // Default ...
    void Default()
    {
        //
        font = "Arial";
        fontSize = 15;
        clr = clrYellow;
        corner = CORNER_RIGHT_LOWER;

        //
        // Presentation ...
        showCandleTime = true;
    }

    //
    // Hide all Visible Buffers ...
    void Hide()
    {
        showCandleTime = false;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            true
            //
            ;

        //
        return result;
    }

    //
    // Retrieve MAx Length ...
    int Max()
    {
        //
        int result = 0;

        //
        result = MathMax(0, 0);

        //
        return result;
    }
};

//
// Class ...
class XCX121XCTHelper : public XCBaseHelper
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructors ...
    XCX121XCTHelper()
        : XCBaseHelper(_Symbol, _Period)
    {
    }

    //
    // Deconstructor ...
    ~XCX121XCTHelper()
    {
        mInputs.Clean();
    }

    //
    // Tools ...
    bool Init(
        string symbol,          // Trading Symbol
        ENUM_TIMEFRAMES period, // Trading Period
        X121XCTInputs &inputs   // Inputs
    )
    {
        //
        bool result = false;

        //
        mSymbol = symbol;
        mPeriod = period;

        //
        result = inputs.IsValid();
        if (!result)
        {
            return result;
        }

        //
        mInputs = inputs;

        //
        mHandler = iCustom(
            mSymbol,
            mPeriod,
            "x-saherelm.x121.xct",
            //
            // Inputs ...
            mInputs.fontSize, // Font Size ...
            mInputs.font,     // Font ...
            mInputs.clr,      // Text Color ...
            mInputs.corner,   // Text Position ...
            //
            // Presentation ...
            "",
            mInputs.showCandleTime // Show Candle Time
            //
        );
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // Inputs ...

    //
    X121XCTInputs GetInputs()
    {
        return mInputs;
    }

    //
    bool SetInputs(
        X121XCTInputs &inputs // Configs
    )
    {
        //
        return Init(
            mSymbol,
            mPeriod,
            inputs
            //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...
    X121XCTInputs mInputs; // Inputs ...
};

//
// Tools ...

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Documents\Indicators\x-saherelm.x121.xcc.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Candle Styles
// ---------------------------------------------------
// Name: X121 XCC
// Description: Candle Styles ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm X121 XCC Indicator"
#property strict

//
// START Constants ...
//

//
// Indicator Short Name ...
#define ShortName "X121 XCC"

//
// Holds an SnapShot of Charts Configuration ...
struct XChartStyle
{
    //
    // chart's ID ...
    long chartId;
    //
    // chart's mode ...
    ENUM_CHART_MODE mode;
    //
    // show bid line ...
    bool showBidLine;
    //
    // show ask line ...
    bool showAskLine;
    //
    // show grids on chart ...
    bool showGrid;
    //
    // show volumes ...
    bool showVolumes;
    //
    // Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders) ...
    bool showTradeLevels;
    //
    // chart autoscroll ...
    bool autoScroll;
    //
    // chart quick navigation state ...
    bool quickNavigation;
    //
    // chart's foreground color ...
    color foreGroundColor;
    //
    // chart's background color ...
    color backGroundColor;
    //
    // Up Color ...
    color upColor;
    //
    // Down Color ...
    color downColor;
    //
    // Bullish color ...
    color bullishColor;
    //
    // Bearish color ...
    color bearishColor;
    //
    // grid color ...
    color gridColor;
    //
    // bid line color ...
    color bidLineColor;
    //
    // ask line color ...
    color askLineColor;
    //
    // line mode and doji candlestick color ...
    color lineColor;
    //
    // Color of stop order levels (Stop Loss and Take Profit) ...
    color stopColor;
    //
    // volumes color ...
    color volumesColor;
};

//
// END Constants ...
//

//
// START Inputs ...
//

//
input group "Chart Style";
input ENUM_CHART_MODE mode = CHART_CANDLES; // Mode
input color upColor = clrGreen;              // Up Color
input color downColor = clrRed;             // Down Color
input color lineColor = clrGreen;            // Line mode and Doji candlestick Color
input color bullishColor = clrGreen;         // Bullish Color
input color bearishColor = clrRed;          // Bearish Color
input color volumesColor = clrGreen;        // Volumes Color

//
input group "Presentation";
input bool showCandles = true; // Show Candles

//
// END Inputs ...
//

//
// START Includes and Imports ...
//

//
// Includes Draw Library ...
#include "../Libraries/x-saherelm.draw.lib.mq5"

//
// Includes Common Library ...
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// END Includes and Imports ...
//

//
// START Buffers ...
//

//
#property indicator_chart_window

//
#property indicator_buffers 0
#property indicator_plots 0

//
// END Buffers ...
//

//
// START Global Definitions: Variables, Properties and etc ...
//

//
int maxLength;

//
XChartStyle chartStyle;
XChartStyle clearStyle;

//
// END Global Definitions: Variables, Properties and etc ...
//

//
// START Event Handlers ...
//

//
// Initialization ...
int OnInit()
{
    //
    // Validate Inputs ...
    if (!ValidateInputs())
    {
        return INIT_PARAMETERS_INCORRECT;
    }

    //
    drawPrefix = ShortName;

    //
    // because in some cases we may have more than one input for
    // calculation and we must prevent any calculation
    // untill we pass the biggest input length, here we get max Input length
    // and then wait until pass it ...
    maxLength = ExtractMaxLengthOfInputs();

    //
    // Define Index Buffers ...
    DefineBuffers();

    //
    // Set Indicator ShortName ...
    SetIndicatorName();

    //
    // Apply Chart Style ...
    ApplyCustomChartStyle();

    //
    // Init Succeed ...
    return INIT_SUCCEEDED;
}

//
// DeInitialization ...
void OnDeinit(const int reason)
{
    //
    // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
    // REASON_REMOVE 1 Program removed from a chart
    // REASON_RECOMPILE 2 Program recompiled
    // REASON_CHARTCHANGE 3 A symbol or a chart period is changed
    // REASON_CHARTCLOSE 4 Chart closed
    // REASON_PARAMETERS 5 Inputs changed by a user
    // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
    // REASON_TEMPLATE 7 Another chart template applied
    // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
    // REASON_CLOSE 9 Terminal closed
}

//
// Calculating what we want ...
int OnCalculate(
    const int rates_total,     // Total Bars on Chart ...
    const int prev_calculated, // Total Calculated Bars on Charts ...
    const datetime &time[],    // History of Open Time ...
    const double &open[],      // History of Open Price ...
    const double &high[],      // History of High Price ...
    const double &low[],       // History of Low Price ...
    const double &close[],     // History of Close Price ...
    const long &tick_volume[], // History of Tick Volumes on Bar ...
    const long &volume[],      // History of Trade Volumes ...
    const int &spread[]        // History of Spread Price ...
)
{
    //
    // Prepare Buffers ...
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(open, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(volume, true);
    ArraySetAsSeries(spread, true);

    //
    // this counts Available Bars ...
    int limit;

    //
    limit = (prev_calculated > rates_total || prev_calculated <= 0) ? rates_total : (rates_total - prev_calculated) + 1;

    //
    // Main Loop ...
    for (int i = limit - 1; i >= 0 && !IsStopped(); i--)
    {
        CalculateBuffers(i);
    }

    //
    return rates_total;
}

//
// END Event Handlers ...
//

//
// START Functions ...
//

//
// Validate Input Args for Initialization ...
bool ValidateInputs()
{
    //
    bool result = true;

    //
    return result;
}

//
// Retrieve all Exists Input Max Length ...
// use for Start Of Drawing ...
int ExtractMaxLengthOfInputs()
{
    //
    int result = 0;

    //
    return result;
}

//
// Define Indexes and Styles ...
void DefineBuffers()
{
}

//
// Set Indicator Short Name and also we can define Buffers Labels ...
void SetIndicatorName()
{
    IndicatorSetString(INDICATOR_SHORTNAME, ShortName);
}

//
// Calculate Buffers ...
void CalculateBuffers(
    int bar_index // Selected Bar Index
)
{
}

//
// Apply Custom Chart Style ...
void ApplyCustomChartStyle()
{
    //
    // Read Current Chart Config and Store it ...
    ReadChartStyle();

    //
    // After Reading Current Chart Style ...
    // we have to Save Configs and Change Styles ...
    clearStyle = chartStyle;

    //
    clearStyle.upColor = CLR_NONE;
    clearStyle.downColor = CLR_NONE;
    clearStyle.lineColor = CLR_NONE;
    clearStyle.bullishColor = CLR_NONE;
    clearStyle.bearishColor = CLR_NONE;

    //
    // Decide to Show or not Candles based on User Input ...
    if (showCandles)
    {
        SetChartStyle(chartStyle);
    }
    else
    {
        SetChartStyle(clearStyle);
    }
}

//
// Read Previous Chart Style ...
void ReadChartStyle()
{
    //
    // Retrieve Current Chart ID ...
    long chartId = ChartID();
    chartStyle.chartId = chartId;

    //
    // Retrieve Chart Mode ...
    chartStyle.mode = (ENUM_CHART_MODE)ChartGetInteger(chartId, CHART_MODE);

    //
    chartStyle.showBidLine = (bool)ChartGetInteger(chartId, CHART_SHOW_BID_LINE);
    chartStyle.showAskLine = (bool)ChartGetInteger(chartId, CHART_SHOW_ASK_LINE);
    chartStyle.showGrid = (bool)ChartGetInteger(chartId, CHART_SHOW_GRID);
    chartStyle.showVolumes = (bool)ChartGetInteger(chartId, CHART_SHOW_VOLUMES);
    chartStyle.showTradeLevels = (bool)ChartGetInteger(chartId, CHART_SHOW_TRADE_LEVELS);
    chartStyle.autoScroll = (bool)ChartGetInteger(chartId, CHART_AUTOSCROLL);
    chartStyle.quickNavigation = (bool)ChartGetInteger(chartId, CHART_QUICK_NAVIGATION);

    //
    chartStyle.foreGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_FOREGROUND);
    chartStyle.backGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_BACKGROUND);
    chartStyle.upColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_UP);
    chartStyle.downColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_DOWN);
    chartStyle.bullishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BULL);
    chartStyle.bearishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BEAR);
    chartStyle.gridColor = (color)ChartGetInteger(chartId, CHART_COLOR_GRID);
    chartStyle.bidLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_BID);
    chartStyle.askLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_ASK);
    chartStyle.stopColor = (color)ChartGetInteger(chartId, CHART_COLOR_STOP_LEVEL);
    chartStyle.lineColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_LINE);
    chartStyle.volumesColor = (color)ChartGetInteger(chartId, CHART_COLOR_VOLUME);

    //
    chartStyle.mode = mode;
    chartStyle.upColor = upColor;
    chartStyle.downColor = downColor;
    chartStyle.lineColor = lineColor;
    chartStyle.bearishColor = bearishColor;
    chartStyle.bullishColor = bullishColor;
    chartStyle.volumesColor = volumesColor;
}

//
// Set Chart Style ...
void SetChartStyle(
    XChartStyle &mChartStyle // Chart Style to Apply
)
{
    //
    ApplyChartStyle(
        mChartStyle.chartId,
        mChartStyle.mode,
        mChartStyle.showBidLine,
        mChartStyle.showAskLine,
        mChartStyle.showGrid,
        mChartStyle.showVolumes,
        mChartStyle.showTradeLevels,
        mChartStyle.autoScroll,
        mChartStyle.quickNavigation,
        mChartStyle.foreGroundColor,
        mChartStyle.backGroundColor,
        mChartStyle.upColor,
        mChartStyle.downColor,
        mChartStyle.bullishColor,
        mChartStyle.bearishColor,
        mChartStyle.gridColor,
        mChartStyle.bidLineColor,
        mChartStyle.askLineColor,
        mChartStyle.lineColor,
        mChartStyle.stopColor,
        mChartStyle.volumesColor);
}

//
// END Functions ...
//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Documents\Indicators\x-saherelm.x121.xct.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Indicator
// ---------------------------------------------
// Name: X121 XCT
// Description: Trend Magic Indicator ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm X121 XCT Indicator"
#property strict

//
// START Constants ...
//

//
#define ShortName "X121 XCT"
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// END Constants ...
//

//
// START Inputs ...
//

//
input int fontSize = 15;                            // Font Size
input string font = "Arial";                        // Font
input color clr = clrYellow;                        // Text Color
input ENUM_BASE_CORNER corner = CORNER_RIGHT_LOWER; // Text Position

//
input group "Presentation";
input bool showTimer = true; // Show Candle Time

//
// END Inputs ...
//

//
// START Buffers ...
//

//
#property indicator_chart_window

//
#property indicator_buffers 0
#property indicator_plots 0

//
// END Buffers ...
//

//
// START Global Definitions: Variables, Properties and etc ...
//
bool isCreatedObject;
string tag;
//
// END Global Definitions: Variables, Properties and etc ...
//

//
// START Event Handlers ...
//

//
// Initialization ...
int OnInit()
{
    //
    // Validate Inputs ...

    //
    tag = ShortName + _Symbol + ToString(_Period);

    //
    // Set Indicator ShortName ...
    SetIndicatorName();

    //
    // Init Succeed ...
    return INIT_SUCCEEDED;
}

//
// DeInitialization ...
void OnDeinit(const int reason)
{
    //
    // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
    // REASON_REMOVE 1 Program removed from a chart
    // REASON_RECOMPILE 2 Program recompiled
    // REASON_CHARTCHANGE 3 A symbol or a chart period is changed
    // REASON_CHARTCLOSE 4 Chart closed
    // REASON_PARAMETERS 5 Inputs changed by a user
    // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
    // REASON_TEMPLATE 7 Another chart template applied
    // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
    // REASON_CLOSE 9 Terminal closed

    //
    // Delete Object ...
    ObjectDelete(0, tag);

    //
    Comment("");
}

//
// Calculating what we want ...
int OnCalculate(
    const int rates_total,     // Total Bars on Chart ...
    const int prev_calculated, // Total Calculated Bars on Charts ...
    const datetime &time[],    // History of Open Time ...
    const double &open[],      // History of Open Price ...
    const double &high[],      // History of High Price ...
    const double &low[],       // History of Low Price ...
    const double &close[],     // History of Close Price ...
    const long &tick_volume[], // History of Tick Volumes on Bar ...
    const long &volume[],      // History of Trade Volumes ...
    const int &spread[]        // History of Spread Price ...
)
{
    //
    if (!showTimer)
    {
        return rates_total;
    }

    //
    XBarRemainsTime barRemains;
    barRemains.Init(
        _Symbol,
        _Period
        //
    );

    //
    string msg = barRemains
                     .ToString(
                         "",
                         "",
                         "",
                         "",
                         "",
                         ":",
                         true
                         //
                     );

    //
    long mSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);

    //
    string _sp = "";
    if (mSpread < 10)
        _sp = "..";
    else if (mSpread < 100)
        _sp = ".";

    //
    string objText = "Spread: " + IntegerToString(mSpread) + _sp + ", Next Bar in: " + msg;

    //
    if (IsValid(tag) && !isCreatedObject)
    {
        CreateIndicatorObject();
    }

    //
    if (isCreatedObject)
    {
        ObjectSetString(0, tag, OBJPROP_TEXT, objText);
    }

    //
    return rates_total;
}

//
// END Event Handlers ...
//

//
// START Functions ...
//

//
// Set Indicator Short Name and also we can define Buffers Labels ...
void SetIndicatorName()
{
    IndicatorSetString(INDICATOR_SHORTNAME, ShortName);
}

//
void CreateIndicatorObject()
{
    //
    if (!showTimer ||
        !IsValid(tag))
    {
        return;
    }

    //
    isCreatedObject = ObjectCreate(0, tag, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, tag, OBJPROP_CORNER, corner);
    ObjectSetInteger(0, tag, OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, tag, OBJPROP_YDISTANCE, 2);

    //
    ENUM_ANCHOR_POINT Anchor = ANCHOR_LEFT_UPPER;
    switch (corner)
    {
    case CORNER_LEFT_UPPER:
        Anchor = ANCHOR_LEFT_UPPER;
        break;
    case CORNER_RIGHT_UPPER:
        Anchor = ANCHOR_RIGHT_UPPER;
        break;
    case CORNER_LEFT_LOWER:
        Anchor = ANCHOR_LEFT_LOWER;
        break;
    case CORNER_RIGHT_LOWER:
        Anchor = ANCHOR_RIGHT_LOWER;
        break;
    }

    //
    int mFontSize = fontSize > 0
                        ? fontSize
                        : 10;

    //
    string mFont = IsValid(font)
                       ? font
                       : "Arial";

    //
    ObjectSetString(0, tag, OBJPROP_FONT, mFont);
    ObjectSetInteger(0, tag, OBJPROP_COLOR, clr);
    ObjectSetInteger(0, tag, OBJPROP_ANCHOR, Anchor);
    ObjectSetInteger(0, tag, OBJPROP_FONTSIZE, mFontSize);
}

//
// END Functions ...
//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Experts\x-saherelm.backtester.ea.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center XTest MQL5 Expert Advisor
// -------------------------------------------------
// Name: XBackTester
// Description: a Backtest Tools Strategy Tester
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////x
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property icon "../Images/SaherElm_Logo_Color.ico"
#property description "SaherElm XBackTester"
#property strict

//
#define ShortName "XBackTester"

//
// Imports ...
#include "../Classes/x-saherelm.x-backtester.expert.class.mq5"

//
// Definitions ...

//
// Inputs ...

//
// Common ...
input group "Common";
input long eaMagicNumber = 78692110; // Magic Number
input int eaSlippage = 10;           // Slippgae
input string eaLogSuffix = "";       // Log Suffix

//
// Alert ...
// Here we Provider All Requirements for
// Configuring Alert Provider ...
input group "Alert";
input bool eaEnableAlerts = true;    // Enable Alerts
input bool eaLogAlerts = true;       // Log Alerts
input bool eaMailAlerts = false;     // Mail Alerts
input bool eaPushAlerts = false;     // Push Alerts
input bool eaTerminalAlerts = false; // Terminal Alerts

//
// Define Local Variables ...

//
string eaLogTag;          // EA Log Prefix ...
XCBackTesterEA *eaExpert; // EA Expert ...

//
// Event Handlers ...

//
// Initialization ...
int OnInit()
{
    //
    // Preparing Tags ...
    string eaTag =
        (IsValid(eaLogSuffix)
             ? eaLogSuffix + "_"
             : "") +
        ShortName;

    //
    // Validate Inputs and Required Parameters
    // for EA to Works fine ...
    if (!ValidateInputs())
    {
        return INIT_PARAMETERS_INCORRECT;
    }

    //
    // Initialize Timer ...
    if (!InitTimer())
    {
        return INIT_FAILED;
    }

    //
    // Configure EA Expert Class ...
    eaExpert = new XCBackTesterEA();

    //
    // Symbol / Period ...
    eaExpert.symbol = _Symbol;
    eaExpert.period = _Period;

    //
    // Common ...
    eaExpert.eaMagicNumber = eaMagicNumber;
    eaExpert.eaSlippage = eaSlippage;
    eaExpert.eaLogSuffix = eaTag;

    //
    // Alert ...
    eaExpert.eaEnableAlerts = eaEnableAlerts;
    eaExpert.eaLogAlerts = eaLogAlerts;
    eaExpert.eaMailAlerts = eaMailAlerts;
    eaExpert.eaPushAlerts = eaPushAlerts;
    eaExpert.eaTerminalAlerts = eaTerminalAlerts;

    //
    // Attach Event Handlers ...
    eaExpert.AddOnDealsChangedEventHandler(OnDealsChanged);
    eaExpert.AddOnStopLossEventHandler(OnStopLossTriggered);
    eaExpert.AddOnOrdersChangedEventHandler(OnOrdersChanged);
    eaExpert.AddOnTakeProfitEventHandler(OnTakeProfitTriggered);
    eaExpert.AddOnModifyPositionEventHandler(OnPositionModified);
    eaExpert.AddOnPositionsChangedEventHandler(OnPositionsChanged);
    eaExpert.AddOnTradeStateChangedEventHandler(OnTradeStateChanged);
    eaExpert.AddOnForceClosePositionEventHandler(OnPositionForceClosed);
    eaExpert.AddOnPartialClosePositionEventHandler(OnPositionPartialClosed);

    //
    // Configure Other Requirements ...
    // eaExpert.volume = 0;
    // eaExpert.riskInPoint = 0;
    // eaExpert.allowedTPSLStep = 0;
    // eaExpert.riskToRewardRatio = 0;
    // eaExpert.minAllowedRiskInPoint = 0;

    //
    // Try to Initialize eaExpert ...
    bool isInited = eaExpert.HandleOnInit();
    if (!isInited)
    {
        //
        Print(ShortName, ": Initialization Failed ...");
        return INIT_FAILED;
    }

    //
    Print(ShortName, ": Initialized Successfully ...");
    return INIT_SUCCEEDED;
}

//
// DeInitialization ...
void OnDeinit(const int reason)
{
    //
    // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
    // REASON_REMOVE 1 Program removed from a chart
    // REASON_RECOMPILE 2 Program recompiled
    // REASON_CHARTCHANGE 3 A symbol or a chart period is changed
    // REASON_CHARTCLOSE 4 Chart closed
    // REASON_PARAMETERS 5 Inputs changed by a user
    // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
    // REASON_TEMPLATE 7 Another chart template applied
    // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
    // REASON_CLOSE 9 Terminal closed

    //
    // Destroy Timer ...
    eaExpert.HandleOnDeInit();

    //
    delete eaExpert;
    ZeroMemory(eaExpert);
}

//
// On Tick Handler ...
void OnTick()
{
    eaExpert.HandleOnTick();
}

//
// Handle On Trade Event ...
void OnTrade()
{
    eaExpert.HandleOnTrade();
}

//
// On Timer ...
void OnTimer()
{
    eaExpert.HandleOnTimer();
}

//
// Chart Event Handler ...
void OnChartEvent(
    const int id,
    const long &lparam,
    const double &dparam,
    const string &sparam //
)
{
    eaExpert.HandleOnChartEvent(id, lparam, dparam, sparam);
}

//
// Custom Functions ...

/**
 * Validate all Inputs of Expert Adviser ...
 *
 * @return ( bool )
 */
bool ValidateInputs()
{
    //
    bool result = false;

    //
    string errMessage = "";

    //
    bool isCommonValid = (
        //
        eaSlippage > 0 &&
        eaMagicNumber > 0
        //
    );
    if (!isCommonValid)
    {
        //
        errMessage += "common configurations error;" + "\n";
    }

    //
    result =
        //
        // Common ...
        isCommonValid &&
        //
        // Otere ...
        true
        //
        ;

    //
    if (!result)
    {
        //
        // Since here Logger not Initiallized, we Use Raw Print Command ...
        Print(ShortName + " Errors: \n", errMessage);
    }

    //
    return result;
}

/**
 * Initialize Timer ...
 *
 * @return ( bool )
 */
bool InitTimer()
{
    //
    bool result = false;

    //
    // TODO: Initialize Timer if necessary ...
    // result = EventSetTimer(1);
    // result = EventSetMillisecondTimer(100);
    result = true;

    //
    return result;
}

/**
 * Destroy Timer ...
 */
void DestroyTimer()
{
    //
    // TODO: Use This ...
    // EventKillTimer();
}

//
// Trade Event Handlers ...

/**
 * Handle StopLoss Triggered Positions ...
 *
 * @param  deal: XDeal instance ...
 */
void OnStopLossTriggered(const XDeal &deal)
{
    eaExpert.HandleOnStopLossTriggered(deal);
}

/**
 * Handle Take Profit Triggered Positions ...
 *
 * @param  deal: XDeal instance ...
 */
void OnTakeProfitTriggered(const XDeal &deal)
{
    eaExpert.HandleOnTakeProfitTriggered(deal);
}

/**
 * Handle Force Close a Position ...
 *
 * @param  ticket: ULONG ...
 * @param  position: XPosition instance ...
 * @param  comment: String ...
 */
void OnPositionForceClosed(
    const ulong ticket,
    const XPosition &position,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionForceClosed(
        ticket,
        position,
        comment //
    );
}

/**
 * Handle Deals Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnDealsChanged(int count)
{
    eaExpert.HandleOnDealsChanged(count);
}

/**
 * Handle Orders Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnOrdersChanged(int count)
{
    eaExpert.HandleOnOrdersChanged(count);
}

/**
 * Handle Positions Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnPositionsChanged(int count)
{
    eaExpert.HandleOnPositionsChanged(count);
}

/**
 * Handle Position Modified ...
 *
 * @param ticket: ulong, Position Ticket ...
 * @param profit: double, Position Profit ...
 * @param comment: string, Comment ...
 */
void OnPositionModified(
    const ulong ticket,
    const double profit,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionModified(
        ticket,
        profit,
        comment //
    );
}

/**
 * Handle Position Partial Closed ...
 *
 * @param ticket: ulong, Position Ticket ...
 * @param profit: double, Position Profit ...
 * @param comment: string, Comment ...
 */
void OnPositionPartialClosed(
    const ulong ticket,
    const double profit,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionPartialClosed(
        ticket,
        profit,
        comment //
    );
}

/**
 * Handle Trade State Changed ...
 *
 * @param state: XOnTradeHandlerState instance ...
 */
void OnTradeStateChanged(const XOnTradeHandlerState &state)
{
    eaExpert.HandleOnTradeStateChanged(state);
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Experts\x-saherelm.x-expert.ea.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Expert Advisor
// -------------------------------------------------
// Name: XBaseEA
// Description: Base Expert Advisor as Template ...
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property icon "../Images/SaherElm_Logo_Color.ico"
#property description "SaherElm Based Expert Advisor"
#property strict

//
#define ShortName "XBaseEA"

//
// Importts ...
#include "../Classes/x-saherelm.x-expert.class.mq5"

//
// Inputs ...

//
// Common ...
input group "Common";
input long eaMagicNumber = 1694056; // Magic Number
input int eaSlippage = 10;          // Slippgae
input string eaLogSuffix = "";      // Log Suffix

//
// Symbols
// [
// Name|
// Symbol|
// period|
// (ST_04:00_08:30),(TL_10:30_12:00),(NY_16:00_20:00)|
// maxAllowedSpread|
// maxAllowedPosition|
// (
//     restrictionsPeriod,
//     restrictionsMultiplier,
//     maxAllowedTPs,
//     maxAllowedSLs,
//     maxAllowedTrades,
//     closeAllOpenTradesAt
// )
// ]
//
//
// [XNAME|XAUUSDb|M5|(ST_04:00_08:30),(TL_10:30_12:00),(NY_16:00_20:00)|25.0|5|(XTRADE_RESTRICATION_DAILY,1,0,2,0)]
input string eaSymbolConfiguration = "[XNAME|XAUUSDb|M5|(ST_04:00_08:30),(TL_10:30_12:00),(NY_16:00_20:00)|25.0|5|(XTRADE_RESTRICATION_DAILY,3,0,2,0)]"; // Symbol Configurations

//
// Management ...
input group "Management";
input bool eaAllowGuards = false;              // Allow Guards
input bool eaAllowTrade = true;                // Allow Trade on Signals
input bool eaAllowLongs = true;                // Allow Long Trades
input bool eaAllowShorts = true;               // Allow Short Trades
input double eaMaxAllowedDrawdownFactor = 0.3; // Max Allowed DrawDown for Trade

//
// Volume ...
input group "Volume";
input ENUM_X_VOLUME_SELECT_TYPE eaVolumeSelect = X_VOLUME_CONSTANT; // Volume Applying Type
input double eaStaticVoluem = 0.0;                                  // Static Volume
input double eaDynamicVolumeStepBalance = 0.0;                      // Step of Balance for Increase Volume
input double eaDynamicVolumeStepVolume = X_MIN_VOLUME;              // Step of Volume Increasing
input double eaConstantRiskBalance = 0.0;                           // Constant Risk Balance per Trade
input double eaConstantPercent = 1;                                 // Constant Percent of Balance Per Trade
input double eaConstantBalance = 1000.0;                            // Constant Balance for Calculations

//
// Alert ...
input group "Alert";
input bool eaEnableAlerts = true;    // Enable Alerts
input bool eaSaveAlerts = true;      // Save Alerts
input bool eaLogAlerts = true;       // Log Alerts
input bool eaMailAlerts = false;     // Mail Alerts
input bool eaPushAlerts = true;      // Push Alerts
input bool eaTerminalAlerts = false; // Terminal Alerts

//
// Reports ...
input group "Reports";
input bool eaReportNewMonths = false;         // Report New Month
input bool eaReportNewWeeks = false;          // Report New Weeks
input bool eaReportNewDays = true;            // Report New Days
input bool eaReportNewHours = false;          // Report New Hours
input bool eaReportTrades = true;             // Report Trades
input bool eaReportSignals = true;            // Report Signals
input bool eaReportRestrictions = true;       // Report Restrictions
input bool eaReportProtector = true;          // Report Protector Actions
input bool eaReportAfterTradesBalance = true; // Report Balance after a Trade Finished

//
// Collector ...
input group "Collector";
input bool eaSaveTrades = true;       // Save Trades
input bool eaSaveSignals = true;      // Save Signals
input bool eaSaveWins = true;         // Save Winning Conditions
input bool eaSaveLosts = true;        // Save Lost Conditions
input bool eaSaveRestrictions = true; // Save Restriction Reports

//
// Management Panel ...
input group "Management Panel";
input bool eaShowPanel = false;           // Show Management Panel
double eaPanelAllowedTPSLStep = 10;       // Value for Change TP/SL in Each Step
double eaPanelMinAllowedRiskInPoint = 20; // Min Allowed Risk in Point
double eaPanelRiskInPoint = 50;           // Risk in Point per Trades
double eaPanelRiskToRewardRatio = 2.0;    // Risk To Reward per Trades
double eaPanelVolume = X_MIN_VOLUME;      // Default volume Per Trade

//
// Variables ...

//
XCBaseExpert *eaExpert;

//
// Event Handlers ...

/**
 * Initialize EA Process ...
 *
 * @return ( int )
 */
int OnInit()
{
    //
    // Preparing Tags ...
    string eaTag =
        (IsValid(eaLogSuffix)
             ? eaLogSuffix + "_"
             : "") +
        ShortName;

    //
    // Validate Inputs and Required Parameters ...

    // for EA to Works fine ...
    if (!ValidateInputs())
    {
        return INIT_PARAMETERS_INCORRECT;
    }

    //
    // Initialize Timer ...
    if (!InitTimer())
    {
        return INIT_FAILED;
    }

    //
    // Configure EA Expert Class ...
    eaExpert = new XCBaseExpert();

    //
    // Setting All Inputs to Expert Adviser Class ...

    //
    // Common ...
    eaExpert.eaMagicNumber = eaMagicNumber;
    eaExpert.eaSlippage = eaSlippage;
    eaExpert.eaLogSuffix = eaTag;

    //
    // Symbols ...
    eaExpert.eaSymbolConfiguration = eaSymbolConfiguration;

    //
    // Management ...
    eaExpert.eaAllowGuards = eaAllowGuards;
    eaExpert.eaAllowTrade = eaAllowTrade;
    eaExpert.eaAllowLongs = eaAllowLongs;
    eaExpert.eaAllowShorts = eaAllowShorts;
    eaExpert.eaMaxAllowedDrawdownFactor = eaMaxAllowedDrawdownFactor;

    //
    // Volume ...
    eaExpert.eaVolumeSelect = eaVolumeSelect;
    eaExpert.eaStaticVoluem = eaStaticVoluem;
    eaExpert.eaDynamicVolumeStepBalance = eaDynamicVolumeStepBalance;
    eaExpert.eaDynamicVolumeStepVolume = eaDynamicVolumeStepVolume;
    eaExpert.eaConstantRiskBalance = eaConstantRiskBalance;
    eaExpert.eaConstantPercent = eaConstantPercent;
    eaExpert.eaConstantBalance = eaConstantBalance;

    //
    // Alert ...
    eaExpert.eaEnableAlerts = eaEnableAlerts;
    eaExpert.eaSaveAlerts = eaSaveAlerts;
    eaExpert.eaLogAlerts = eaLogAlerts;
    eaExpert.eaMailAlerts = eaMailAlerts;
    eaExpert.eaPushAlerts = eaPushAlerts;
    eaExpert.eaTerminalAlerts = eaTerminalAlerts;

    //
    // Reports ...
    eaExpert.eaReportNewMonths = eaReportNewMonths;
    eaExpert.eaReportNewWeeks = eaReportNewWeeks;
    eaExpert.eaReportNewDays = eaReportNewDays;
    eaExpert.eaReportNewHours = eaReportNewHours;
    eaExpert.eaReportTrades = eaReportTrades;
    eaExpert.eaReportSignals = eaReportSignals;
    eaExpert.eaReportRestrictions = eaReportRestrictions;
    eaExpert.eaReportProtector = eaReportProtector;
    eaExpert.eaReportAfterTradesBalance = eaReportAfterTradesBalance;

    //
    // Collector ...
    eaExpert.eaSaveTrades = eaSaveTrades;
    eaExpert.eaSaveSignals = eaSaveSignals;
    eaExpert.eaSaveWins = eaSaveWins;
    eaExpert.eaSaveLosts = eaSaveLosts;
    eaExpert.eaSaveRestrictions = eaSaveRestrictions;

    //
    // Management Panel ...
    eaExpert.eaShowPanel = eaShowPanel;
    eaExpert.eaPanelAllowedTPSLStep = eaPanelAllowedTPSLStep;
    eaExpert.eaPanelMinAllowedRiskInPoint = eaPanelMinAllowedRiskInPoint;
    eaExpert.eaPanelRiskInPoint = eaPanelRiskInPoint;
    eaExpert.eaPanelRiskToRewardRatio = eaPanelRiskToRewardRatio;
    eaExpert.eaPanelVolume = eaPanelVolume;

    //
    // Attach Event Handlers ...
    eaExpert.AddOnSignalEventHandler(OnSignalTriggered);
    eaExpert.onGuardedEventHandler = OnGuardedTriggered;
    eaExpert.AddOnDealsChangedEventHandler(OnDealsChanged);
    eaExpert.AddOnStopLossEventHandler(OnStopLossTriggered);
    eaExpert.AddOnOrdersChangedEventHandler(OnOrdersChanged);
    eaExpert.AddOnSignalExecutedEventHandler(OnSignalExecuted);
    eaExpert.canAnalyseEventHandler = CanAnalyseEventTriggered;
    eaExpert.AddOnTakeProfitEventHandler(OnTakeProfitTriggered);
    eaExpert.checkForGuardEventHandler = CheckForGuardTriggered;
    eaExpert.AddOnModifyPositionEventHandler(OnPositionModified);
    eaExpert.AddOnPositionsChangedEventHandler(OnPositionsChanged);
    eaExpert.AddOnTradeStateChangedEventHandler(OnTradeStateChanged);
    eaExpert.AddOnForceClosePositionEventHandler(OnPositionForceClosed);
    eaExpert.AddOnPartialClosePositionEventHandler(OnPositionPartialClosed);

    //
    // Try to Initialize eaExpert ...
    bool isInited = eaExpert.HandleOnInit();
    if (!isInited)
    {
        //
        Print(ShortName, ": Initialization Failed ...");
        return INIT_FAILED;
    }

    //
    Print(ShortName, ": Initialized Successfully ...");
    return INIT_SUCCEEDED;
}

/**
 * De Initialize Expert ...
 *
 * @param  reason: int, DeInitialization Reason ...
 *
 * REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
 * REASON_REMOVE 1 Program removed from a chart
 * REASON_RECOMPILE 2 Program recompiled
 * REASON_CHARTCHANGE 3 A symbol or a chart period is changed
 * REASON_CHARTCLOSE 4 Chart closed
 * REASON_PARAMETERS 5 Inputs changed by a user
 * REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
 * REASON_TEMPLATE 7 Another chart template applied
 * REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
 * REASON_CLOSE 9 Terminal closed
 *
 */
void OnDeinit(const int reason)
{
    //
    // Destroy Timer ...
    // If Initialized ...

    //
    // De Init Expert Class Instance ...
    eaExpert.HandleOnDeInit();

    //
    // Cleanup Resources ...
    delete eaExpert;
    ZeroMemory(eaExpert);
}

/**
 * Handle Tick for Processing ...
 */
void OnTick()
{
    eaExpert.HandleOnTick();
}

/**
 * Handle On Trade Processing ...
 */
void OnTrade()
{
    eaExpert.HandleOnTrade();
}

/**
 * Handle On Timer Processing ...
 */
void OnTimer()
{
    eaExpert.HandleOnTimer();
}

/**
 * Handle On Chart Event Processing ...

 * @param  id: int, Specified Event ID ...
 * @param  lparam: long, type event parameter
 * @param  dparam: double, type event parameter
 * @param  sparam: string, type event parameter
 */
void OnChartEvent(
    const int id,
    const long &lparam,
    const double &dparam,
    const string &sparam //
)
{
    eaExpert.HandleOnChartEvent(id, lparam, dparam, sparam);
}

//
// Event Listeners ...

/**
 * Handle CanAnalyse Event Triggered ...
 *
 * @param  symbol: Symbol ...
 * @param  period: Period ...
 * @param  time: Analysing Time ...
 *
 * @return ( bool )
 */
bool CanAnalyseEventTriggered(
    string symbol,
    ENUM_TIMEFRAMES period,
    datetime time //
)
{
    return eaExpert.HandleCanAnalyseEventTriggered(
        symbol,
        period,
        time //
    );
}

/**
 * Check For Guard ...
 *
 * @param  guards: XGuard, reference Collection to holds result ...
 * @param  positions: XPosition, refrence collection to Provides Positions ...
 * @param  barIndex: int, Specified Bar index ...
 *
 * @return ( int )
 */
int CheckForGuardTriggered(
    XGuard &guards[],
    XPosition &positions[],
    int barIndex = 0 //
)
{
    //
    return eaExpert.CheckForGuardTriggered(
        guards,
        positions,
        barIndex //
    );
}

/**
 * Handle Guard Events ...
 *
 * @param  action: ENUM_X_GUARD_ACTIONS
 * @param  positions: XPosition[]
 */
void OnGuardedTriggered(
    ENUM_X_GUARD_ACTIONS action,
    XPosition &positions[] //
)
{
    eaExpert.OnGuardedTriggered(action, positions);
}

/**
 * Trigger OnSignal Event ...
 *
 * @param  signal: XSignal
 */
void OnSignalTriggered(XSignal &signal)
{
    eaExpert.HandleOnSignalTriggered(signal);
}

/**
 * Trigger OnSignal Executed Event ...
 *
 * @param  signal: XSignal
 */
void OnSignalExecuted(XSignal &signal)
{
    eaExpert.HandleOnSignalExecuted(signal);
}

/**
 * Handle StopLoss Triggered Positions ...
 *
 * @param  deal: XDeal instance ...
 */
void OnStopLossTriggered(const XDeal &deal)
{
    eaExpert.HandleOnStopLossTriggered(deal);
}

/**
 * Handle Take Profit Triggered Positions ...
 *
 * @param  deal: XDeal instance ...
 */
void OnTakeProfitTriggered(const XDeal &deal)
{
    eaExpert.HandleOnTakeProfitTriggered(deal);
}

/**
 * Handle Force Close a Position ...
 *
 * @param  ticket: ULONG ...
 * @param  position: XPosition instance ...
 * @param  comment: String ...
 */
void OnPositionForceClosed(
    const ulong ticket,
    const XPosition &position,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionForceClosed(
        ticket,
        position,
        comment //
    );
}

/**
 * Handle Deals Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnDealsChanged(int count)
{
    eaExpert.HandleOnDealsChanged(count);
}

/**
 * Handle Orders Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnOrdersChanged(int count)
{
    eaExpert.HandleOnOrdersChanged(count);
}

/**
 * Handle Positions Changes ...
 *
 * @param  count: Integer, Number of Changes ...
 */
void OnPositionsChanged(int count)
{
    eaExpert.HandleOnPositionsChanged(count);
}

/**
 * Handle Position Modified ...
 *
 * @param ticket: ulong, Position Ticket ...
 * @param profit: double, Position Profit ...
 * @param comment: string, Comment ...
 */
void OnPositionModified(
    const ulong ticket,
    const double profit,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionModified(
        ticket,
        profit,
        comment //
    );
}

/**
 * Handle Position Partial Closed ...
 *
 * @param ticket: ulong, Position Ticket ...
 * @param profit: double, Position Profit ...
 * @param comment: string, Comment ...
 */
void OnPositionPartialClosed(
    const ulong ticket,
    const double profit,
    const string comment //
)
{
    //
    eaExpert.HandleOnPositionPartialClosed(
        ticket,
        profit,
        comment //
    );
}

/**
 * Handle Trade State Changed ...
 *
 * @param state: XOnTradeHandlerState instance ...
 */
void OnTradeStateChanged(const XOnTradeHandlerState &state)
{
    eaExpert.HandleOnTradeStateChanged(state);
}

//
// Custom Functions ...

/**
 * Validate all Inputs of Expert Adviser ...
 *
 * @return ( bool )
 */
bool ValidateInputs()
{
    //
    bool result = false;

    //
    string errMessage = "";

    //
    bool isCommonValid = (
        //
        eaSlippage > 0 &&
        eaMagicNumber > 0
        //
    );
    if (!isCommonValid)
    {
        //
        errMessage += "common configurations error;" + "\n";
    }

    //
    result =
        //
        // Common ...
        isCommonValid &&
        //
        // Otere ...
        true
        //
        ;

    //
    if (!result)
    {
        //
        // Since here Logger not Initiallized, we Use Raw Print Command ...
        Print(ShortName + " Errors: \n", errMessage);
    }

    //
    return result;
}

/**
 * Initialize Timer ...
 *
 * @return ( bool )
 */
bool InitTimer()
{
    //
    bool result = false;

    //
    // TODO: Initialize Timer if necessary ...
    // result = EventSetTimer(1);
    // result = EventSetMillisecondTimer(100);
    result = true;

    //
    return result;
}

/**
 * Destroy Timer ...
 */
void DestroyTimer()
{
    //
    // TODO: Use This ...
    // EventKillTimer();
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Helpers\x-saherelm.x121.xcc.helper.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Helper Class Library
// ----------------------------------------------
// Name: XSCX121XCCHelper
// Description: provides all Indicator
// Helper requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-helper.class.mq5"

//
// Definitions ...

//
// Input Models ...
struct X121XCCInputs
{
    //
    // Props ...

    //
    // Chart Style ...
    ENUM_CHART_MODE mode; // Mode
    color upColor;        // Up Color
    color downColor;      // Down Color
    color lineColor;      // Line mode and Doji candlestick Color
    color bearishColor;   // Bullish Color
    color bullishColor;   // Bearish Color
    color volumesColor;   // Volumes Color

    //
    // Presentation ...
    bool showCandles; // Show Candles

    //
    // Constructor(s) ...
    X121XCCInputs()
    {
        //
        Clean();
    }

    //
    // Tools ...

    //
    // Clean ...
    void Clean()
    {
        //
        mode = CHART_CANDLES;

        //
        upColor = CLR_NONE;
        downColor = CLR_NONE;
        lineColor = CLR_NONE;
        bearishColor = CLR_NONE;
        bullishColor = CLR_NONE;
        volumesColor = CLR_NONE;

        //
        showCandles = false;

        //
        ZeroMemory(this);
    }

    //
    // Default ...
    void Default()
    {
        //
        mode = CHART_CANDLES;

        //
        upColor = clrLime;
        downColor = clrRed;
        lineColor = clrLime;
        bearishColor = clrRed;
        bullishColor = clrLime;
        volumesColor = clrGreen;
        //
        showCandles = true;
    }

    //
    // Hide all Visible Buffers ...
    void Hide()
    {
        showCandles = false;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            true
            //
            ;

        //
        return result;
    }

    //
    // Retrieve MAx Length ...
    int Max()
    {
        //
        int result = 0;

        //
        result = MathMax(0, 0);

        //
        return result;
    }
};

//
// Class ...
class XCX121XCCHelper : public XCBaseHelper
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructors ...
    XCX121XCCHelper()
        : XCBaseHelper(_Symbol, _Period)
    {
    }

    //
    // Deconstructor ...
    ~XCX121XCCHelper()
    {
        mInputs.Clean();
    }

    //
    // Tools ...
    bool Init(
        string symbol,          // Trading Symbol
        ENUM_TIMEFRAMES period, // Trading Period
        X121XCCInputs &inputs   // Inputs
    )
    {
        //
        bool result = false;

        //
        mSymbol = symbol;
        mPeriod = period;

        //
        result = inputs.IsValid();
        if (!result)
        {
            return result;
        }

        //
        mInputs = inputs;

        //
        mHandler = iCustom(
            mSymbol,
            mPeriod,
            "x-saherelm.x121.xcc",
            //
            // Inputs ...
            //
            // Chart Style ...
            "",
            mInputs.mode,         // Mode
            mInputs.upColor,      // Up Color
            mInputs.downColor,    // Down Color
            mInputs.lineColor,    // Line mode and Doji candlestick Color
            mInputs.bullishColor, // Bearish Color
            mInputs.bearishColor, // Bullish Color
            mInputs.volumesColor, // Volumes Color
            //
            // Presentation ...
            "",
            mInputs.showCandles // Show Candles
            //
        );
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // Inputs ...

    //
    X121XCCInputs GetInputs()
    {
        return mInputs;
    }

    //
    bool SetInputs(
        X121XCCInputs &inputs // Configs
    )
    {
        //
        return Init(
            mSymbol,
            mPeriod,
            inputs
            //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...
    X121XCCInputs mInputs; // Inputs ...
};

//
// Tools ...

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Helpers\x-saherelm.x121.xct.helper.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Helper Class Library
// ----------------------------------------------
// Name: XSCX121XCTHelper
// Description: provides all Indicator
// Helper requirements ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.x-helper.class.mq5"

//
// Definitions ...

//
// Input Models ...
struct X121XCTInputs
{
    //
    // Props ...

    //
    string font;             // Font
    int fontSize;            // Font Size;
    color clr;               // Text Color
    ENUM_BASE_CORNER corner; // Text Position

    //
    // Presentation ...
    bool showCandleTime; // Show Candle Time

    //
    // Constructor(s) ...
    X121XCTInputs()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Clean ...
    void Clean()
    {
        //
        font = NULL;
        fontSize = 15;
        clr = CLR_NONE;
        corner = CORNER_RIGHT_LOWER;

        //
        // Presentation ...
        showCandleTime = false;

        //
        ZeroMemory(this);
    }

    //
    // Default ...
    void Default()
    {
        //
        font = "Arial";
        fontSize = 15;
        clr = clrYellow;
        corner = CORNER_RIGHT_LOWER;

        //
        // Presentation ...
        showCandleTime = true;
    }

    //
    // Hide all Visible Buffers ...
    void Hide()
    {
        showCandleTime = false;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            true
            //
            ;

        //
        return result;
    }

    //
    // Retrieve MAx Length ...
    int Max()
    {
        //
        int result = 0;

        //
        result = MathMax(0, 0);

        //
        return result;
    }
};

//
// Class ...
class XCX121XCTHelper : public XCBaseHelper
{
    //
    // Public ...
  public:
    //
    // Props ...

    //
    // Constructors ...
    XCX121XCTHelper()
        : XCBaseHelper(_Symbol, _Period)
    {
    }

    //
    // Deconstructor ...
    ~XCX121XCTHelper()
    {
        mInputs.Clean();
    }

    //
    // Tools ...
    bool Init(
        string symbol,          // Trading Symbol
        ENUM_TIMEFRAMES period, // Trading Period
        X121XCTInputs &inputs   // Inputs
    )
    {
        //
        bool result = false;

        //
        mSymbol = symbol;
        mPeriod = period;

        //
        result = inputs.IsValid();
        if (!result)
        {
            return result;
        }

        //
        mInputs = inputs;

        //
        mHandler = iCustom(
            mSymbol,
            mPeriod,
            "x-saherelm.x121.xct",
            //
            // Inputs ...
            mInputs.fontSize, // Font Size ...
            mInputs.font,     // Font ...
            mInputs.clr,      // Text Color ...
            mInputs.corner,   // Text Position ...
            //
            // Presentation ...
            "",
            mInputs.showCandleTime // Show Candle Time
            //
        );
        result = mHandler != INVALID_HANDLE;
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // Inputs ...

    //
    X121XCTInputs GetInputs()
    {
        return mInputs;
    }

    //
    bool SetInputs(
        X121XCTInputs &inputs // Configs
    )
    {
        //
        return Init(
            mSymbol,
            mPeriod,
            inputs
            //
        );
    }

    //
    // Protected ...
  protected:
    //
    // Private ...
  private:
    //
    // Props ...
    X121XCTInputs mInputs; // Inputs ...
};

//
// Tools ...

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Indicators\x-saherelm.x121.xcc.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Candle Styles
// ---------------------------------------------------
// Name: X121 XCC
// Description: Candle Styles ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm X121 XCC Indicator"
#property icon "../Images/SaherElm_Logo_Color.ico"
#property strict

//
// START Constants ...
//

//
// Indicator Short Name ...
#define ShortName "X121 XCC"

//
// Holds an SnapShot of Charts Configuration ...
struct XChartStyle
{
    //
    // chart's ID ...
    long chartId;
    //
    // chart's mode ...
    ENUM_CHART_MODE mode;
    //
    // show bid line ...
    bool showBidLine;
    //
    // show ask line ...
    bool showAskLine;
    //
    // show grids on chart ...
    bool showGrid;
    //
    // show volumes ...
    bool showVolumes;
    //
    // Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders) ...
    bool showTradeLevels;
    //
    // chart autoscroll ...
    bool autoScroll;
    //
    // chart quick navigation state ...
    bool quickNavigation;
    //
    // chart's foreground color ...
    color foreGroundColor;
    //
    // chart's background color ...
    color backGroundColor;
    //
    // Up Color ...
    color upColor;
    //
    // Down Color ...
    color downColor;
    //
    // Bullish color ...
    color bullishColor;
    //
    // Bearish color ...
    color bearishColor;
    //
    // grid color ...
    color gridColor;
    //
    // bid line color ...
    color bidLineColor;
    //
    // ask line color ...
    color askLineColor;
    //
    // line mode and doji candlestick color ...
    color lineColor;
    //
    // Color of stop order levels (Stop Loss and Take Profit) ...
    color stopColor;
    //
    // volumes color ...
    color volumesColor;
};

//
// END Constants ...
//

//
// START Inputs ...
//

//
input group "Chart Style";
input ENUM_CHART_MODE mode = CHART_CANDLES; // Mode
input color upColor = clrGreen;              // Up Color
input color downColor = clrRed;             // Down Color
input color lineColor = clrGreen;            // Line mode and Doji candlestick Color
input color bullishColor = clrGreen;         // Bullish Color
input color bearishColor = clrRed;          // Bearish Color
input color volumesColor = clrGreen;        // Volumes Color

//
input group "Presentation";
input bool showCandles = true; // Show Candles

//
// END Inputs ...
//

//
// START Includes and Imports ...
//

//
// Includes Draw Library ...
#include "../Libraries/x-saherelm.draw.lib.mq5"

//
// Includes Common Library ...
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// END Includes and Imports ...
//

//
// START Buffers ...
//

//
#property indicator_chart_window

//
#property indicator_buffers 0
#property indicator_plots 0

//
// END Buffers ...
//

//
// START Global Definitions: Variables, Properties and etc ...
//

//
int maxLength;

//
XChartStyle chartStyle;
XChartStyle clearStyle;

//
// END Global Definitions: Variables, Properties and etc ...
//

//
// START Event Handlers ...
//

//
// Initialization ...
int OnInit()
{
    //
    // Validate Inputs ...
    if (!ValidateInputs())
    {
        return INIT_PARAMETERS_INCORRECT;
    }

    //
    drawPrefix = ShortName;

    //
    // because in some cases we may have more than one input for
    // calculation and we must prevent any calculation
    // untill we pass the biggest input length, here we get max Input length
    // and then wait until pass it ...
    maxLength = ExtractMaxLengthOfInputs();

    //
    // Define Index Buffers ...
    DefineBuffers();

    //
    // Set Indicator ShortName ...
    SetIndicatorName();

    //
    // Apply Chart Style ...
    ApplyCustomChartStyle();

    //
    // Init Succeed ...
    return INIT_SUCCEEDED;
}

//
// DeInitialization ...
void OnDeinit(const int reason)
{
    //
    // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
    // REASON_REMOVE 1 Program removed from a chart
    // REASON_RECOMPILE 2 Program recompiled
    // REASON_CHARTCHANGE 3 A symbol or a chart period is changed
    // REASON_CHARTCLOSE 4 Chart closed
    // REASON_PARAMETERS 5 Inputs changed by a user
    // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
    // REASON_TEMPLATE 7 Another chart template applied
    // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
    // REASON_CLOSE 9 Terminal closed
}

//
// Calculating what we want ...
int OnCalculate(
    const int rates_total,     // Total Bars on Chart ...
    const int prev_calculated, // Total Calculated Bars on Charts ...
    const datetime &time[],    // History of Open Time ...
    const double &open[],      // History of Open Price ...
    const double &high[],      // History of High Price ...
    const double &low[],       // History of Low Price ...
    const double &close[],     // History of Close Price ...
    const long &tick_volume[], // History of Tick Volumes on Bar ...
    const long &volume[],      // History of Trade Volumes ...
    const int &spread[]        // History of Spread Price ...
)
{
    //
    // Prepare Buffers ...
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(open, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(volume, true);
    ArraySetAsSeries(spread, true);

    //
    // this counts Available Bars ...
    int limit;

    //
    limit = (prev_calculated > rates_total || prev_calculated <= 0) ? rates_total : (rates_total - prev_calculated) + 1;

    //
    // Main Loop ...
    for (int i = limit - 1; i >= 0 && !IsStopped(); i--)
    {
        CalculateBuffers(i);
    }

    //
    return rates_total;
}

//
// END Event Handlers ...
//

//
// START Functions ...
//

//
// Validate Input Args for Initialization ...
bool ValidateInputs()
{
    //
    bool result = true;

    //
    return result;
}

//
// Retrieve all Exists Input Max Length ...
// use for Start Of Drawing ...
int ExtractMaxLengthOfInputs()
{
    //
    int result = 0;

    //
    return result;
}

//
// Define Indexes and Styles ...
void DefineBuffers()
{
}

//
// Set Indicator Short Name and also we can define Buffers Labels ...
void SetIndicatorName()
{
    IndicatorSetString(INDICATOR_SHORTNAME, ShortName);
}

//
// Calculate Buffers ...
void CalculateBuffers(
    int bar_index // Selected Bar Index
)
{
}

//
// Apply Custom Chart Style ...
void ApplyCustomChartStyle()
{
    //
    // Read Current Chart Config and Store it ...
    ReadChartStyle();

    //
    // After Reading Current Chart Style ...
    // we have to Save Configs and Change Styles ...
    clearStyle = chartStyle;

    //
    clearStyle.upColor = CLR_NONE;
    clearStyle.downColor = CLR_NONE;
    clearStyle.lineColor = CLR_NONE;
    clearStyle.bullishColor = CLR_NONE;
    clearStyle.bearishColor = CLR_NONE;

    //
    // Decide to Show or not Candles based on User Input ...
    if (showCandles)
    {
        SetChartStyle(chartStyle);
    }
    else
    {
        SetChartStyle(clearStyle);
    }
}

//
// Read Previous Chart Style ...
void ReadChartStyle()
{
    //
    // Retrieve Current Chart ID ...
    long chartId = ChartID();
    chartStyle.chartId = chartId;

    //
    // Retrieve Chart Mode ...
    chartStyle.mode = (ENUM_CHART_MODE)ChartGetInteger(chartId, CHART_MODE);

    //
    chartStyle.showBidLine = (bool)ChartGetInteger(chartId, CHART_SHOW_BID_LINE);
    chartStyle.showAskLine = (bool)ChartGetInteger(chartId, CHART_SHOW_ASK_LINE);
    chartStyle.showGrid = (bool)ChartGetInteger(chartId, CHART_SHOW_GRID);
    chartStyle.showVolumes = (bool)ChartGetInteger(chartId, CHART_SHOW_VOLUMES);
    chartStyle.showTradeLevels = (bool)ChartGetInteger(chartId, CHART_SHOW_TRADE_LEVELS);
    chartStyle.autoScroll = (bool)ChartGetInteger(chartId, CHART_AUTOSCROLL);
    chartStyle.quickNavigation = (bool)ChartGetInteger(chartId, CHART_QUICK_NAVIGATION);

    //
    chartStyle.foreGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_FOREGROUND);
    chartStyle.backGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_BACKGROUND);
    chartStyle.upColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_UP);
    chartStyle.downColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_DOWN);
    chartStyle.bullishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BULL);
    chartStyle.bearishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BEAR);
    chartStyle.gridColor = (color)ChartGetInteger(chartId, CHART_COLOR_GRID);
    chartStyle.bidLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_BID);
    chartStyle.askLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_ASK);
    chartStyle.stopColor = (color)ChartGetInteger(chartId, CHART_COLOR_STOP_LEVEL);
    chartStyle.lineColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_LINE);
    chartStyle.volumesColor = (color)ChartGetInteger(chartId, CHART_COLOR_VOLUME);

    //
    chartStyle.mode = mode;
    chartStyle.upColor = upColor;
    chartStyle.downColor = downColor;
    chartStyle.lineColor = lineColor;
    chartStyle.bearishColor = bearishColor;
    chartStyle.bullishColor = bullishColor;
    chartStyle.volumesColor = volumesColor;
}

//
// Set Chart Style ...
void SetChartStyle(
    XChartStyle &mChartStyle // Chart Style to Apply
)
{
    //
    ApplyChartStyle(
        mChartStyle.chartId,
        mChartStyle.mode,
        mChartStyle.showBidLine,
        mChartStyle.showAskLine,
        mChartStyle.showGrid,
        mChartStyle.showVolumes,
        mChartStyle.showTradeLevels,
        mChartStyle.autoScroll,
        mChartStyle.quickNavigation,
        mChartStyle.foreGroundColor,
        mChartStyle.backGroundColor,
        mChartStyle.upColor,
        mChartStyle.downColor,
        mChartStyle.bullishColor,
        mChartStyle.bearishColor,
        mChartStyle.gridColor,
        mChartStyle.bidLineColor,
        mChartStyle.askLineColor,
        mChartStyle.lineColor,
        mChartStyle.stopColor,
        mChartStyle.volumesColor);
}

//
// END Functions ...
//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Indicators\x-saherelm.x121.xct.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Indicator
// ---------------------------------------------
// Name: X121 XCT
// Description: Trend Magic Indicator ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://saherelm.ir"
#property version "1.00"
#property description "SaherElm X121 XCT Indicator"
#property icon "../Images/SaherElm_Logo_Color.ico"
#property strict

//
// START Constants ...
//

//
#define ShortName "X121 XCT"
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// END Constants ...
//

//
// START Inputs ...
//

//
input int fontSize = 15;                            // Font Size
input string font = "Arial";                        // Font
input color clr = clrYellow;                        // Text Color
input ENUM_BASE_CORNER corner = CORNER_RIGHT_LOWER; // Text Position

//
input group "Presentation";
input bool showTimer = true; // Show Candle Time

//
// END Inputs ...
//

//
// START Buffers ...
//

//
#property indicator_chart_window

//
#property indicator_buffers 0
#property indicator_plots 0

//
// END Buffers ...
//

//
// START Global Definitions: Variables, Properties and etc ...
//
bool isCreatedObject;
string tag;
//
// END Global Definitions: Variables, Properties and etc ...
//

//
// START Event Handlers ...
//

//
// Initialization ...
int OnInit()
{
    //
    // Validate Inputs ...

    //
    tag = ShortName + _Symbol + ToXString(_Period);

    //
    // Set Indicator ShortName ...
    SetIndicatorName();

    //
    // Init Succeed ...
    return INIT_SUCCEEDED;
}

//
// DeInitialization ...
void OnDeinit(const int reason)
{
    //
    // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
    // REASON_REMOVE 1 Program removed from a chart
    // REASON_RECOMPILE 2 Program recompiled
    // REASON_CHARTCHANGE 3 A symbol or a chart period is changed
    // REASON_CHARTCLOSE 4 Chart closed
    // REASON_PARAMETERS 5 Inputs changed by a user
    // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
    // REASON_TEMPLATE 7 Another chart template applied
    // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
    // REASON_CLOSE 9 Terminal closed

    //
    // Delete Object ...
    ObjectDelete(0, tag);

    //
    Comment("");
}

//
// Calculating what we want ...
int OnCalculate(
    const int rates_total,     // Total Bars on Chart ...
    const int prev_calculated, // Total Calculated Bars on Charts ...
    const datetime &time[],    // History of Open Time ...
    const double &open[],      // History of Open Price ...
    const double &high[],      // History of High Price ...
    const double &low[],       // History of Low Price ...
    const double &close[],     // History of Close Price ...
    const long &tick_volume[], // History of Tick Volumes on Bar ...
    const long &volume[],      // History of Trade Volumes ...
    const int &spread[]        // History of Spread Price ...
)
{
    //
    if (!showTimer)
    {
        return rates_total;
    }

    //
    XBarRemainsTime barRemains;
    barRemains.Init(
        _Symbol,
        _Period
        //
    );

    //
    string msg = barRemains
                     .ToString(
                         "",
                         "",
                         "",
                         "",
                         "",
                         ":",
                         true
                         //
                     );

    //
    long mSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);

    //
    string _sp = "";
    if (mSpread < 10)
        _sp = "..";
    else if (mSpread < 100)
        _sp = ".";

    //
    string objText = "Spread: " + IntegerToString(mSpread) + _sp + ", Next Bar in: " + msg;

    //
    if (IsValid(tag) && !isCreatedObject)
    {
        CreateIndicatorObject();
    }

    //
    if (isCreatedObject)
    {
        ObjectSetString(0, tag, OBJPROP_TEXT, objText);
    }

    //
    return rates_total;
}

//
// END Event Handlers ...
//

//
// START Functions ...
//

//
// Set Indicator Short Name and also we can define Buffers Labels ...
void SetIndicatorName()
{
    IndicatorSetString(INDICATOR_SHORTNAME, ShortName);
}

//
void CreateIndicatorObject()
{
    //
    if (!showTimer ||
        !IsValid(tag))
    {
        return;
    }

    //
    isCreatedObject = ObjectCreate(0, tag, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, tag, OBJPROP_CORNER, corner);
    ObjectSetInteger(0, tag, OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, tag, OBJPROP_YDISTANCE, 2);

    //
    ENUM_ANCHOR_POINT Anchor = ANCHOR_LEFT_UPPER;
    switch (corner)
    {
    case CORNER_LEFT_UPPER:
        Anchor = ANCHOR_LEFT_UPPER;
        break;
    case CORNER_RIGHT_UPPER:
        Anchor = ANCHOR_RIGHT_UPPER;
        break;
    case CORNER_LEFT_LOWER:
        Anchor = ANCHOR_LEFT_LOWER;
        break;
    case CORNER_RIGHT_LOWER:
        Anchor = ANCHOR_RIGHT_LOWER;
        break;
    }

    //
    int mFontSize = fontSize > 0
                        ? fontSize
                        : 10;

    //
    string mFont = IsValid(font)
                       ? font
                       : "Arial";

    //
    ObjectSetString(0, tag, OBJPROP_FONT, mFont);
    ObjectSetInteger(0, tag, OBJPROP_COLOR, clr);
    ObjectSetInteger(0, tag, OBJPROP_ANCHOR, Anchor);
    ObjectSetInteger(0, tag, OBJPROP_FONTSIZE, mFontSize);
}

//
// END Functions ...
//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.common.extensions.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Common Library
// --------------------------------------
// Name: XCommonExtensionsLib
// Description: provide all commonly used functions ...
//
//
// 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 "../Classes/x-saherelm.x-md5.class.mq5"
#include "../Libraries/x-saherelm.x-enums.lib.mq5"

//
#include <MovingAverages.mqh>

//
// Constants ...
#define X_MIN_SIZE 0
#define X_INVALID_INDEX -1

//
// START Value Checkers ...
//

//
// Check a Value Not Empty ...
template <typename T>
bool NotEmpty(T value)
{
    return value != EMPTY_VALUE;
}

//
// Check a Value Not Empty and Zero ...
template <typename T>
bool NotEmptyZero(T value)
{
    return value != 0 &&
           value != EMPTY_VALUE;
}

//
// END Value Checkers ...
//

//
// START Validators ...
//

/**
 * Validate Strings ...
 *
 * @param  value: string
 *
 * @return ( bool )
 */
bool IsValid(string value)
{
    //
    bool result = false;

    //
    result =
        //
        value != NULL &&
        StringLen(value) > 0
        //
        ;

    //
    return result;
}
bool IsXValid(string value)
{
    return IsValid(value);
}
bool IsSpecifiedValid(string value)
{
    return IsValid(value);
}

/**
 * Validate Date ...
 *
 * @param  value: datetime
 *
 * @return ( bool )
 */
bool IsValid(datetime value)
{
    //
    bool result = false;

    //
    result = NotEmpty(value) && value != NULL;

    //
    return result;
}
bool IsXValid(datetime value)
{
    return IsValid(value);
}
bool IsSpecifiedValid(datetime value)
{
    return IsValid(value);
}

/**
 * Validate Time Frame ...
 *
 * @param  value: ENUM_TIMEFRAMES member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_TIMEFRAMES value)
{
    //
    bool result = false;

    //
    result =
        //
        value != NULL
        //
        ;

    //
    return result;
}
bool IsXValid(ENUM_TIMEFRAMES value)
{
    //
    return IsValid(value);
}
bool IsSpecifiedValid(ENUM_TIMEFRAMES value)
{
    //
    return IsValid(value);
}

/**
 * Validate Specific Period Mode ...
 *
 * @param  mMethod: ENUM_X_PERIOD_METHOD member, Period Select Method ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Selected Period ...
 *
 * @return ( bool )
 */
bool IsValid(
    ENUM_X_PERIOD_METHOD mMethod,
    ENUM_TIMEFRAMES mPeriod //
)
{
    //
    bool result = false;

    //
    result =
        //
        mMethod == X_PERIOD_NONE
            ? false
        : mMethod == X_PERIOD_MANUALLY
            ? mPeriod != NULL
        : mMethod == X_PERIOD_AUTO
            ? true
            : false
        //
        ;

    //
    return result;
}
bool IsXValid(
    ENUM_X_PERIOD_METHOD mMethod,
    ENUM_TIMEFRAMES mPeriod //
)
{
    return IsValid(
        mMethod,
        mPeriod //
    );
}
bool IsSpecifiedValid(
    ENUM_X_PERIOD_METHOD mMethod,
    ENUM_TIMEFRAMES mPeriod //
)
{
    return IsValid(
        mMethod,
        mPeriod //
    );
}

/**
 * Validate a Size Value ...
 *
 * @param  value: int, Provided Size ...
 *
 * @return ( bool )
 */
bool IsValidSize(int value)
{
    //
    bool result = value > X_MIN_SIZE;

    //
    return result;
}

/**
 * Check a Given Index is Valid or not ...
 *
 * @param  value: int, Provided Index ...
 *
 * @return ( bool )
 */
bool IsValidIndex(int value)
{
    return value >= X_MIN_SIZE;
}

//
// END Validators ...
//

//
// START Normalizations ...
//

/**
 * Normalize Symbol ...
 *
 * @param  value: string, Provided Symbols ...
 *
 * @return ( string )
 */
string NormalizeSymbol(string value)
{
    //
    string result = value;

    //
    if (!IsValid(result))
    {
        result = _Symbol;
    }

    //
    return result;
}

/**
 * Normalize Time ...
 *
 * @param  value: datetime, Provided Time ...
 *
 * @return ( datetime )
 */
datetime NormalizeTime(datetime value)
{
    //
    datetime result = value;

    //
    if (!IsValid(result))
    {
        result = TimeCurrent();
    }

    //
    return result;
}

/**
 * Normalize Period ...
 *
 * @param  value: ENUM_TIMEFRAMES member, Provided Period ...
 *
 * @return ( ENUM_TIMEFRAMES )
 */
ENUM_TIMEFRAMES NormalizePeriod(ENUM_TIMEFRAMES value)
{
    //
    ENUM_TIMEFRAMES result = value;

    //
    if (!IsValid(value))
    {
        result = _Period;
    }

    //
    return result;
}

/**
 * Normalize Market Cycle  ...
 *
 * @param  value: ENUM_X_MARKET_CYCLES, Provided Cycle ...
 *
 * @return ( ENUM_X_MARKET_CYCLES )
 */
ENUM_X_MARKET_CYCLES NormalizeCycle(ENUM_X_MARKET_CYCLES value)
{
    //
    ENUM_X_MARKET_CYCLES result = value;

    //
    if (!IsValid(result))
    {
        result = X_MARKET_CYCLE_SHORT;
    }

    //
    return value;
}

/**
 * Normalize Price Boundary  ...
 *
 * @param  value: ENUM_X_BOUNDARY_PRICE, Provided Boundary Mode ...
 *
 * @return ( ENUM_X_BOUNDARY_PRICE )
 */
ENUM_X_BOUNDARY_PRICE NormalizeBoundary(ENUM_X_BOUNDARY_PRICE value)
{
    //
    ENUM_X_BOUNDARY_PRICE result = value;

    //
    if (!IsValid(result))
    {
        result = X_BOUNDARY_PRICE_HIGH_LOW;
    }

    //
    return result;
}

/**
 * Normalize Integer Value ...
 *
 * @param  value: int, Provided Value ...
 * @param  min: int, Minimum Allowed Value ...
 * @param  max: int, Maximum Allowed Value ...
 *
 * @return ( int )
 */
int NormalizeInt(
    int value,
    int min = 0,
    int max = INT_MAX //
)
{
    //
    if (value < min)
    {
        value = min;
    }

    //
    if (value > max)
    {
        value = max;
    }

    //
    return value;
}

/**
 * Normalize Double Value ...
 *
 * @param  value: double, Provided Value ...
 * @param  min: double, Minimum Allowed Value ...
 * @param  max: double, Maximum Allowed Value ...
 *
 * @return ( double )
 */
double NormalizeDouble(
    double value,
    double min = 0,
    double max = DBL_MAX //
)
{
    //
    if (value < min)
    {
        value = min;
    }

    //
    if (value > max)
    {
        value = max;
    }

    //
    return value;
}
double NormalizeXDouble(
    double value,
    double min = 0,
    double max = DBL_MAX //
)
{
    return NormalizeDouble(value, min, max);
}

/**
 * Normalize Index based on Collection ...
 *
 * @param  index: int, Provided Index ...
 * @param  buffer: Collection Reference ...
 */
template <typename T>
bool NormalizeIndex(
    int &index,
    T &buffer[] //
)
{
    //
    bool result = false;

    //
    int size = ArraySize(buffer);
    result = IsValidSize(size);
    if (!result)
    {
        return result;
    }

    //
    if (!IsValidIndex(index))
    {
        index = X_MIN_SIZE;
    }

    //
    if (index > size - 1)
    {
        index = size - 1;
    }

    //
    result = IsValidIndex(index) &&
             index >= X_MIN_SIZE &&
             index < size;

    //
    return result;
}

/**
 * Normalize Start and Count based on Specified Buffer ...
 *
 * @param  start: int, Provided Start index ...
 * @param  count: int, Provided Number of Iteration ...
 * @param  buffer: Collection instance ...
 */
template <typename T>
void NormalizeCount(
    int &start,
    int &count,
    T &buffer[] //
)
{
    //
    // Validate Buffer Size ...
    int bufferSize = ArraySize(buffer);
    if (!IsValidSize(bufferSize))
    {
        return;
    }

    //
    // Normalization Start ...
    NormalizeIndex(
        start,
        buffer //
    );

    //
    if (count < start)
    {
        count = (start - count);
    }

    //
    // Normalization Count ...

    //
    if (count < 0)
    {
        count = start;
    }

    //
    if (count > bufferSize - start)
    {
        count = bufferSize - start;
    }
}

/**
 * Normalize a Volume ...
 *
 * @param  mVolume: double, Provided Volume ...
 * @param  mSymbol: string, Specified Symbol ...
 * @param  mLength: int, Provided Default floating Length ...
 *
 * @return ( double )
 */
double NormalizeVolume(
    double mVolume,
    string mSymbol = NULL,
    int mLength = 2 //
)
{
    //
    double result = mVolume;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double maxAvailableVolume = SymbolInfoDouble(mSymbol, SYMBOL_VOLUME_MAX);
    double minAvailableVolume = SymbolInfoDouble(mSymbol, SYMBOL_VOLUME_MIN);

    //
    // Normalize Digits ...
    int digits = GetDigits(minAvailableVolume);

    //
    mLength =
        mLength == 0 || mLength > digits
            ? digits
            : MathMin(mLength, digits);

    //
    // Normalize Volume ...
    result = NormalizeDouble(result, mLength);

    //
    // Validate Result ...
    if (result > maxAvailableVolume)
    {
        result = maxAvailableVolume;
    }
    else if (result < minAvailableVolume)
    {
        result = minAvailableVolume;
    }

    //
    return result;
}

/**
 * Normalize a Price Value ...
 *
 * @param  mPrice: double, Provided Price ...
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
double NormalizePrice(
    double mPrice,
    string mSymbol = NULL //
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double tickSize = 0;
    result = SymbolInfoDouble(mSymbol, SYMBOL_TRADE_TICK_SIZE, tickSize);

    //
    int digits = GetDigits(mSymbol);
    result = NormalizeDouble(MathRound(mPrice / tickSize) * tickSize, digits);

    //
    return result;
}

/**
 * Normalize Chart Id ...
 *
 * @param  _chartId: ulong
 *
 * @return ( ulong )
 */
ulong NormalizeChartId(ulong _chartId = 0)
{
    //
    if (_chartId <= 0)
    {
        _chartId = ChartID();
    }

    //
    return _chartId;
}

//
// END Normalizations ...
//

//
// START Position ...
//

/**
 * Determine a Position type is Long or not ...
 *
 * @param  value: ENUM_POSITION_TYPE member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsLong(ENUM_POSITION_TYPE value)
{
    //
    bool result = value == POSITION_TYPE_BUY;

    //
    return result;
}

/**
 * Determine a Position type is Long or not ...
 *
 * @param  value: ENUM_ORDER_TYPE member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsLong(ENUM_ORDER_TYPE value)
{
    //
    bool result = value == ORDER_TYPE_BUY;

    //
    return result;
}

/**
 * Determine a Position type is Long or not ...
 *
 * @param  value: ENUM_X_DIRECTION member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsLong(ENUM_X_DIRECTION value)
{
    //
    bool result = value == X_DIRECTION_BULLISH;

    //
    return result;
}

/**
 * Determine a Position type is Short or not ...
 *
 * @param  value: ENUM_POSITION_TYPE member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsShort(ENUM_POSITION_TYPE value)
{
    //
    bool result = value == POSITION_TYPE_SELL;

    //
    return result;
}

/**
 * Determine a Position type is Short or not ...
 *
 * @param  value: ENUM_ORDER_TYPE member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsShort(ENUM_ORDER_TYPE value)
{
    //
    bool result = value == ORDER_TYPE_SELL;

    //
    return result;
}

/**
 * Determine a Position type is Short or not ...
 *
 * @param  value: ENUM_X_DIRECTION member, Specified Type ...
 *
 * @return ( bool )
 */
bool IsShort(ENUM_X_DIRECTION value)
{
    //
    bool result = value == X_DIRECTION_BEARISH;

    //
    return result;
}

/**
 * Converts Opposit Direction Type ...
 *
 * @param  value: ENUM_POSITION_TYPE member, Specified Type ...
 *
 * @return ( ENUM_POSITION_TYPE )
 */
ENUM_POSITION_TYPE Opposit(ENUM_POSITION_TYPE value)
{
    //
    ENUM_POSITION_TYPE result = NULL;

    //
    bool isLong = IsLong(value);
    result = isLong
                 ? POSITION_TYPE_SELL
                 : POSITION_TYPE_BUY;

    //
    return result;
}

/**
 * Converts Opposit Direction Type ...
 *
 * @param  value: ENUM_ORDER_TYPE member, Specified Type ...
 *
 * @return ( ENUM_ORDER_TYPE )
 */
ENUM_ORDER_TYPE Opposit(ENUM_ORDER_TYPE value)
{
    //
    ENUM_ORDER_TYPE result = NULL;

    //
    bool isLong = IsLong(value);
    result = isLong
                 ? ORDER_TYPE_SELL
                 : ORDER_TYPE_BUY;

    //
    return result;
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_POSITION_TYPE member ...
 *
 * @return ( string )
 */
string ToXString(ENUM_POSITION_TYPE value)
{
    //
    string result = NULL;

    //
    result =
        //
        IsLong(value)
            ? "Long"
            : "Short"
        //
        ;

    //
    return result;
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_ORDER_TYPE member ...
 *
 * @return ( string )
 */
string ToXString(ENUM_ORDER_TYPE value)
{
    //
    string result = NULL;

    //
    result =
        //
        IsLong(value)
            ? "Long"
            : "Short"
        //
        ;

    //
    return result;
}

/**
 * Get Ask Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
double GetAsk(
    string mSymbol = NULL //
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double result = SymbolInfoDouble(mSymbol, SYMBOL_ASK);

    //
    return result;
}

/**
 * Get Bid Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
double GetBid(
    string mSymbol = NULL //
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double result = SymbolInfoDouble(mSymbol, SYMBOL_BID);

    //
    return result;
}

/**
 * Get Spread Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
double GetSpread(
    string mSymbol = NULL //
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double ask = GetAsk(mSymbol);
    double bid = GetBid(mSymbol);

    //
    double result = MathAbs(ask - bid);

    //
    return result;
}
double GetXSpread(
    string mSymbol = NULL //
)
{
    return GetSpread(mSymbol);
}

/**
 * Get SPecified Symbols Volume Step ...
 *
 * @param  mSymbol: string ...
 *
 * @return ( double )
 */
double GetVolumeStep(
    string mSymbol = NULL //
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double result = SymbolInfoDouble(mSymbol, SYMBOL_VOLUME_STEP);

    //
    return result;
}

/**
 * Get Point Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
double GetPoints(
    string mSymbol = NULL // Trading Symbol
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double result = SymbolInfoDouble(mSymbol, SYMBOL_POINT);

    //
    return result;
}
double GetXPoints(string mSymbol = NULL)
{
    return GetPoints(mSymbol);
}

/**
 * Converts Price to Point ...
 *
 * @param  price: double, price ...
 * @param  mSymbol: string, Symbol ...
 *
 * @return ( double )
 */
double PriceToPoint(
    double mPrice,        // the price amount which required to calculate
    string mSymbol = NULL // Trading Symbol
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);
    mPrice = NormalizePrice(mPrice, mSymbol);

    //
    double point = GetPoints(mSymbol);
    result = mPrice / point;

    //
    return result;
}

/**
 * Get Digits Length ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 *
 * @return ( double )
 */
int GetDigits(
    string mSymbol = NULL // Trading Symbol
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    int result = (int)SymbolInfoInteger(mSymbol, SYMBOL_DIGITS);

    //
    return result;
}

/**
 * Get Entry Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  type: ENUM_POSITION_TYPE member, Specified Entry type ...
 *
 * @return ( double )
 */
double GetEntry(
    string mSymbol = NULL,
    ENUM_POSITION_TYPE type = POSITION_TYPE_BUY //
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    result =
        IsLong(type)
            ? GetAsk(mSymbol)
            : GetBid(mSymbol);
    result = NormalizePrice(result, mSymbol);

    //
    return result;
}
double GetXEntry(
    string mSymbol = NULL,
    ENUM_POSITION_TYPE type = POSITION_TYPE_BUY //
)
{
    return GetEntry(mSymbol, type);
}

/**
 * Get Entry Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  type: ENUM_X_DIRECTION member, Specified Entry type ...
 *
 * @return ( double )
 */
double GetEntry(
    string mSymbol = NULL,
    ENUM_X_DIRECTION type = X_DIRECTION_BULLISH //
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    result =
        IsBullish(type)
            ? GetAsk(mSymbol)
            : GetBid(mSymbol);
    result = NormalizePrice(result, mSymbol);

    //
    return result;
}
double GetXEntry(
    string mSymbol = NULL,
    ENUM_X_DIRECTION type = X_DIRECTION_BULLISH //
)
{
    return GetEntry(mSymbol, type);
}

/**
 * Get Exit Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  type: ENUM_POSITION_TYPE member, Specified Exit type ...
 *
 * @return ( double )
 */
double GetExit(
    string mSymbol = NULL,
    ENUM_POSITION_TYPE type = POSITION_TYPE_BUY //
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    result =
        IsLong(type)
            ? GetBid(mSymbol)
            : GetAsk(mSymbol);
    result = NormalizePrice(result, mSymbol);

    //
    return result;
}
double GetXExit(
    string mSymbol = NULL,
    ENUM_POSITION_TYPE type = POSITION_TYPE_BUY //
)
{
    return GetExit(mSymbol, type);
}

/**
 * Get Exit Price ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  type: ENUM_X_DIRECTION member, Specified Exit type ...
 *
 * @return ( double )
 */
double GetExit(
    string mSymbol = NULL,
    ENUM_X_DIRECTION type = X_DIRECTION_BULLISH //
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    result =
        IsBullish(type)
            ? GetBid(mSymbol)
            : GetAsk(mSymbol);
    result = NormalizePrice(result, mSymbol);

    //
    return result;
}
double GetXExit(
    string mSymbol = NULL,
    ENUM_X_DIRECTION type = X_DIRECTION_BULLISH //
)
{
    return GetExit(mSymbol, type);
}

/**
 * Retrieve Current Tick ...
 */
bool GetTick(
    string mSymbol,
    MqlTick &tick //
)
{
    return SymbolInfoTick(mSymbol, tick);
}

//
// Peak and Vales ...

int FindXSwing(
    ENUM_X_SWING_TYPE mType,
    string mSymbol = NULL,
    ENUM_TIMEFRAMES mPeriod = NULL,
    int mFromIndex = 0,
    int mShoulders = 5 //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!IsXValid(mType))
    {
        return result;
    }

    //
    result = mFromIndex;
    int foundedIDX = FindXNextSwing(
        mType,
        mSymbol,
        mPeriod,
        result - mShoulders,
        mShoulders * 2 + 1 //
    );
    while (foundedIDX != result)
    {
        //
        result = FindXNextSwing(
            mType,
            mSymbol,
            mPeriod,
            result + 1,
            mShoulders //
        );

        //
        foundedIDX = FindXNextSwing(
            mType,
            mSymbol,
            mPeriod,
            result - mShoulders,
            mShoulders * 2 + 1 //
        );
    }

    //
    return result;
}

int FindXNextSwing(
    ENUM_X_SWING_TYPE mType,
    string mSymbol = NULL,
    ENUM_TIMEFRAMES mPeriod = NULL,
    int mFromIndex = 0,
    int mShoulders = 5 //
)
{
    //
    int result = -1;

    //
    if (mFromIndex < 0)
    {
        mShoulders += mFromIndex;
    }

    //
    // Normalize ...
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);
    mFromIndex = NormalizeInt(mFromIndex, 0);
    mShoulders = NormalizeInt(mShoulders, 3);

    //
    // Validate ....
    if (!IsXValid(mType))
    {
        return result;
    }

    //
    // Converts Swing Type to Series Mode ...
    ENUM_SERIESMODE mMode = ToSeriesMode(mType);
    result =
        mMode == MODE_HIGH
            ? iHighest(
                  mSymbol,
                  mPeriod,
                  mMode,
                  mShoulders,
                  mFromIndex //
                  )
            : iLowest(
                  mSymbol,
                  mPeriod,
                  mMode,
                  mShoulders,
                  mFromIndex //
              );

    //
    return result;
}

//
// END Position ...
//

//
// START Tools ...
//

/**
 * Check Expert Running in Testing Mode or not ...
 *
 * @return ( bool )
 */
bool IsRunningOnTestMode()
{
    //
    bool result = false;

    //
    // check programm mode ...
    result = MQLInfoInteger(MQL_TESTER);

    //
    return result;
}

/**
 * Check Expert Running in Visual Testing Mode ...
 *
 * @return ( bool )
 */
bool IsRunningeOnTestVisualMode()
{
    //
    bool result = false;

    //
    result = IsRunningOnTestMode() &&
             MQLInfoInteger(MQL_VISUAL_MODE);

    //
    return result;
}

/**
 * Find Chart ID ...
 *
 * @param  mSymbol: string, Specified Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Specified Period ...
 *
 * @return ( long )
 */
long FindChartID(
    string mSymbol = NULL,
    ENUM_TIMEFRAMES mPeriod = NULL //
)
{
    //
    long result = ChartFirst();
    long first = result;

    //
    while (result > 0)
    {
        //
        string chSymbol = ChartSymbol(result);
        ENUM_TIMEFRAMES chPeriod = ChartPeriod(result);

        //
        bool isOwn =
            //
            chSymbol == mSymbol &&
            chPeriod == mPeriod
            //
            ;
        if (isOwn)
        {
            break;
        }

        //
        result = ChartNext(result);
    }

    //
    return result;
}

/**
 * Retrieve Available Symbols ...
 *
 * @param  symbols: string Collection reference to Hold Symbols ...
 * @param  onlyInWatchList: bool, Force Extracts only Watch List Symbols ...
 *
 * @return ( int )
 */
int GetAllSymbols(
    string &symbols[],           // Hold Result
    bool onlyInWatchList = false // Specified Retrieve only Symbols which in Watch List
)
{
    //
    int result = 0;

    //
    Clean(symbols);

    //
    int count = SymbolsTotal(onlyInWatchList);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        string iSymbol = SymbolName(
            i,
            onlyInWatchList //
        );
        if (!IsValid(iSymbol))
        {
            continue;
        }

        //
        Add(
            iSymbol,
            symbols //
        );
    }

    //
    result = ArraySize(symbols);

    //
    return result;
}

/**
 * Filter Symbols Collections ...
 *
 * @param  query: string, Provided Query to Search Symbols ...
 * @param  symbols: string, Collection resources to Hold's Filtered results ...
 * @param  onlyInWatchList: bool, Specified Apply Filter only on Watch List Symbols ...
 * @param  ignoreCase: bool, Ignore Case Sesitive ...
 * @param  querySeparator: string, Provided Separator of Query ...
 *
 * @return ( int )
 */
int FilterSymbols(
    string query,
    string &symbols[],
    bool onlyInWatchList = false,
    bool ignoreCase = true,
    string querySeparator = "," // If Provided means query is a Collection of Queries and need to be Splitted
)
{
    //
    int result = 0;

    //
    Clean(symbols);

    //
    if (!IsValid(query))
    {
        return result;
    }

    //
    // Check Query is an String array or not ...
    string mQueries[];
    int queriesCount = 0;
    if (IsValid(querySeparator) && Contains(querySeparator, query, true))
    {
        //
        queriesCount = SplitContent(
            mQueries,
            query,
            querySeparator //
        );
    }

    //
    string allSymbols[];
    int allSymbolsCount = GetAllSymbols(
        allSymbols,
        onlyInWatchList //
    );
    if (allSymbolsCount <= 0)
    {
        return result;
    }

    //
    // Loop Through all Symbols ...
    for (int i = 0; i < allSymbolsCount; i++)
    {
        //
        string iSymbol = allSymbols[i];

        //
        bool isFilterPassed = false;
        if (queriesCount == 0)
        {
            //
            isFilterPassed = Contains(
                query,
                iSymbol,
                ignoreCase //
            );
        }
        else if (queriesCount > 0)
        {
            //
            isFilterPassed = Contains(
                mQueries,
                iSymbol,
                ignoreCase //
            );
        }

        //
        if (isFilterPassed)
        {
            //
            Add(
                iSymbol,
                symbols //
            );
        }
    }

    //
    result = ArraySize(symbols);

    //
    return result;
}

/**
 * Extract Symbols to Find Only USD Side Assets ...
 *
 * @param  symbols: string, Collection resources to Hold's Filtered results ...
 * @param  onlyInWatchList: bool, Specified Apply Filter only on Watch List Symbols ...
 * @param  ignoreCase: bool, Ignore Case Sesitive ...
 * @param  querySeparator: string, Provided Separator of Query ...
 *
 * @return ( int )
 */
int FilterUSDSymbols(
    string &symbols[],
    bool onlyInWatchList = false,
    bool ignoreCase = true,
    string querySeparator = "," //
)
{
    //
    return FilterSymbols(
        "USD",
        symbols,
        onlyInWatchList,
        ignoreCase,
        querySeparator //
    );
}

/**
 * Get Digits Length of Specified Double Value ...
 *
 * @param  value: double, Provided Value ...
 *
 * @return ( int )
 */
int GetDigits(double value)
{
    //
    int d = 0;
    double p = 1;
    while (MathRound(value * p) / p != value)
    {
        p = MathPow(10, ++d);
    }

    //
    return d;
}

/**
 * Retrieve Specific Object Types Name ...
 *
 * @param  t: T object reference ...
 *
 * @return ( string )
 *
 */
template <typename T>
string GetTypeName(const T &t)
{
    return typename(T);
}
template <typename T>
string GetTypeName(const T *t)
{
    return typename(T);
}

/**
 * Get Specified Types Token ...
 *
 * @param  item: T, object reference ...
 *
 * @return ( string )
 */
template <typename T>
string GetToken(T &item)
{
    //
    string mType = GetTypeName(item);

    //
    string result = mType;

    //
    string parts[];
    int partsCount = SplitContent(
        parts,
        result,
        " " //
    );
    if (partsCount <= 0)
    {
        //
        result = mType;
        return result;
    }

    //
    bool hasLastPart = GetLastItem(
        result,
        parts //
    );
    if (!hasLastPart)
    {
        //
        result = mType;
        return result;
    }

    //
    return result;
}
template <typename T>
string GetSpecificToken(T &item)
{
    return GetToken(item);
}

/**
 * Generate Unique Tags ...
 *
 * @param  model: T, object reference ...
 *
 * @return ( string )
 */
template <typename T>
string GenerateTag(T &model)
{
    //
    string result = "";

    //
    string tag = model.ToXString();
    if (StringLen(tag) == 0)
    {
        return result;
    }

    //
    XSCMD5 *md5;
    md5 = new XCMD5();

    //
    result = md5.Hash(tag);

    //
    ZeroMemory(md5);

    //
    return result;
}

/**
 * Hash Specified Content ...
 *
 * @param  content: T, Proveded Content ...
 *
 * @return ( string )
 */
template <typename T>
string ToMD5(T content)
{
    //
    string result = "";

    //
    string strContent = ToXString(content);
    if (StringLen(strContent) == 0)
    {
        return result;
    }

    //
    XCMD5 *md5;
    md5 = new XCMD5();

    //
    result = md5.Hash(strContent);

    //
    delete md5;
    ZeroMemory(md5);

    //
    return result;
}

/**
 * Calculate Fibo Level ...
 *
 * @param  mUp: double, Specified Upper Price ...
 * @param  mDown: double, Specified Lower Price ...
 * @param  mLevel: ENUM_X_FIBO_LEVELS member, Specified Fibo Level ...
 * @param  mForDir: ENUM_X_DIRECTION member, Specified Calculation Direction ...
 *
 * @return ( double )
 */
double GetFibonacciLevel(
    double mUp,
    double mDown,
    ENUM_X_FIBO_LEVELS mLevel,
    ENUM_X_DIRECTION mForDir //
)
{
    //
    double result = 0;

    //
    double levelValue = GetFiboLevelValue(mLevel);

    //
    bool isValid =
        //
        mUp > 0 &&
        mDown > 0 &&
        levelValue > 0 &&
        mUp > mDown &&
        HasDirection(mForDir)
        //
        ;
    if (!isValid)
    {
        return result;
    }

    //
    bool isBullish = IsBullish(mForDir);

    //
    double ling = mUp - mDown;
    double pLevel = (ling / 100) * (levelValue * 100);

    //
    result =
        isBullish
            ? mUp - pLevel
            : mDown + pLevel;

    //
    return result;
}

/**
 * Calculate Sspecified Buffer's Moving Averages ...
 *
 * @param  rates_total: Integer, number of items ...
 * @param  prev_calculated: Integer, Previous Calculate Items ...
 * @param  begin: Integer, Bar Index ...
 * @param  period: Integer, Moving Average Length ...
 * @param  source: Double Array, Specified Array for Calculating Moving Averages ...
 * @param  buffer: Double Array, Specified Array to Apply Calculated Moving Average on it ...
 * @param  method: ENUM_MA_METHOD member Specified Moving Average Calculation Method ...
 *
 * @return ( int )
 */
int iMAOnBuffer(
    const int _rates_total,
    const int _prev_calculated,
    const int _begin,
    const int _period,
    const double &_source[],
    double &_buffer[],
    ENUM_X_MA_METHOD _method = X_MA_MODE_SMA //
)
{
    //
    int result = 0;

    //
    if (
        _period < 0 ||
        _rates_total <= 0 ||
        _prev_calculated < 0 ||
        _method == X_MA_MODE_NONE ||
        _prev_calculated > _rates_total)
    {
        return result;
    }

    //
    switch (_method)
    {
    //
    case X_MA_MODE_SMA:
        result = SimpleMAOnBuffer(
            _rates_total,
            _prev_calculated,
            _begin,
            _period,
            _source,
            _buffer //
        );
        break;

    //
    case X_MA_MODE_EMA:
        result = ExponentialMAOnBuffer(
            _rates_total,
            _prev_calculated,
            _begin,
            _period,
            _source,
            _buffer //
        );
        break;

    //
    case X_MA_MODE_LWMA:
        result = LinearWeightedMAOnBuffer(
            _rates_total,
            _prev_calculated,
            _begin,
            _period,
            _source,
            _buffer //
        );
        break;

    //
    case X_MA_MODE_SMMA:
        result = SmoothedMAOnBuffer(
            _rates_total,
            _prev_calculated,
            _begin,
            _period,
            _source,
            _buffer //
        );
        break;
    }

    //
    return result;
}

//
// END Tools ...
//

//
// START String ...
//

/**
 * Search a Content Contains Specific String or not ...
 *
 * @param  mQuery: string, Search Query ...
 * @param  mContent: string, Search Content ...
 * @param  ignoreCase: bool, Force Ignore Case Sensitive ...
 *
 * @return ( bool )
 */
bool Contains(
    string mQuery,
    string mContent,
    bool ignoreCase = true //
)
{
    //
    bool result = false;

    //
    // Validate Query ...
    result = IsValid(mQuery);
    if (!result)
    {
        return result;
    }

    //
    // Validate Content ...
    result = IsValid(mContent);
    if (!result)
    {
        return result;
    }

    //
    // Normalize Inputs ...
    string query = mQuery;
    string content = mContent;
    if (ignoreCase)
    {
        //
        StringToLower(query);
        StringToLower(content);
    }

    //
    int queryIdx = StringFind(
        content,
        query);

    //
    result = queryIdx >= 0;

    //
    return result;
}

/**
 * Search a Content Contains Specifics Query or not ...
 *
 * @param  mQueries: string collection, Search Queries ...
 * @param  mContent: string, Search Content ...
 * @param  ignoreCase: bool, Force Ignore Case Sensitive ...
 *
 * @return ( bool )
 */
bool Contains(
    string &mQueries[],
    string mContent,
    bool ignoreCase = true //
)
{
    //
    bool result = false;

    //
    // Validate Queries ...
    int queriesCount = ArraySize(mQueries);
    result = queriesCount > 0;
    if (!result)
    {
        return result;
    }

    //
    // Validate Content ...
    result = IsValid(mContent);
    if (!result)
    {
        return result;
    }

    //
    int containsCount = 0;
    for (int i = 0; i < queriesCount; i++)
    {
        //
        bool isContains = Contains(
            mQueries[i],
            mContent,
            ignoreCase);

        //
        if (!isContains)
        {
            //
            result = false;
            break;
        }

        //
        containsCount++;
    }

    //
    result = containsCount == queriesCount;

    //
    return result;
}

/**
 * Search a Content and Extract Occuranse Indexes ...
 *
 * @param  mQuery: string, Search Query ...
 * @param  mContent: string, Search Content ...
 * @param  result: int collection reference, Holds Founded Indexes ...
 * @param  ignoreCase: bool, Force Ignore Case Sensitive ...
 *
 * @return ( int )
 */
int FindIndexes(
    string mQuery,
    string mContent,
    int &result[],
    bool ignoreCase = false //
)
{
    //
    int mResult = 0;

    //
    Clean(result);

    //
    // Validate Inputs ...
    if (!IsValid(mQuery) ||
        !IsValid(mContent))
    {
        return mResult;
    }

    //
    // Apply Ignore Case if Provided ...
    if (ignoreCase)
    {
        //
        bool isConverted = StringToLower(mQuery);
        if (!isConverted)
        {
            return mResult;
        }

        //
        isConverted = StringToLower(mContent);
        if (!isConverted)
        {
            return mResult;
        }
    }

    //
    // Search First Index ...
    int lastPos = 0;
    int idx = StringFind(
        mContent,
        mQuery,
        lastPos //
    );
    while (idx >= 0)
    {
        //
        // Add Position to Result ...
        Add(
            idx,
            result //
        );

        //
        lastPos = idx + StringLen(mQuery);
        idx = StringFind(
            mContent,
            mQuery,
            lastPos //
        );
    }

    //
    mResult = ArraySize(result);

    //
    return mResult;
}
int FindXIndexes(
    string mQuery,
    string mContent,
    int &result[],
    bool ignoreCase = false //
)
{
    return FindXIndexes(mQuery, mContent, result, ignoreCase);
}

/**
 * Convert From String ...
 *
 * @param  value: string, Provided Value ...
 *
 * @return ( T )
 */
template <typename T>
T FromString(string value)
{
    //
    T result = NULL;

    //
    if (!IsValid(value))
    {
        return result;
    }

    //
    result = (T)value;

    //
    return result;
}

/**
 * Converts to String ...
 *
 * @param  value: T, Provided Value ...
 *
 * @return ( string )
 */
template <typename T>
string ToXString(T value)
{
    return (string)value;
}

string Trim(string value)
{
    //
    string result = NULL;

    //
    result = value;

    //
    StringTrimRight(result);
    StringTrimLeft(result);

    //
    return result;
}

//
// Used In Report or Summary Generators ...
string ToXString(
    string prefix,                     // Title
    bool value,                        // Value
    bool ignoreFalseConditions = true, // Ignore False Conditions
    string lineSeparator = "\n",       // Line Separator
    string prefixSeparator = ":"       // Title Separator
)
{
    //
    string result = NULL;

    //
    bool canSet =
        IsBoolean(ToXString(value))
            ? (bool)value || !ignoreFalseConditions
            : true;

    //
    result = canSet
                 ? prefix +
                       prefixSeparator + " " +
                       ToXString(value) +
                       lineSeparator
                 : "";

    //
    return result;
}

//
template <typename T>
string ToXString(
    string prefix,               // Title
    T value,                     // Value
    string lineSeparator = "\n", // Line Separator
    string prefixSeparator = ":" // Title Separator
)
{
    //
    string result = NULL;

    //
    result = prefix +
             prefixSeparator + " " +
             ToXString(value) +
             lineSeparator;

    //
    return result;
}

//
string SetLabel(
    string mLabel,              // Provided Label ...
    string value,               // Source Value ...
    string separator = "\n",    // Line Separator
    bool addTopEmptyLine = true // Add Empty Line On top
)
{
    //
    string result = value;

    //
    if (StringLen(mLabel) <= 0)
    {
        return result;
    }

    //
    result = (StringLen(value) > 0)
                 ? (addTopEmptyLine ? separator : "") +
                       mLabel +
                       separator +
                       "-----------------------------" +
                       separator +
                       value
                 : value;

    //
    return result;
}

//
// Converts a Buffer to String Representation ...
template <typename T>
string ToXString(
    const T &buffer[],          // Specified Buffer
    string separator = ",",     // Specified Separator
    bool addIndex = false,      // Specified Add Items Indexes or Not
    string indexLabel = "i:",   // Specified Index Label
    string indexSeparator = ">" // Specified Index Separator
)
{
    //
    string result = "";

    //
    int bufferSize = ArraySize(buffer);
    if (bufferSize <= 0)
    {
        return result;
    }

    //
    for (int i = 0; i < bufferSize; i++)
    {
        //
        if (addIndex)
        {
            //
            if (StringLen(indexLabel) > 0)
            {
                result += indexLabel + " ";
            }

            //
            result += (string)i + " ";

            //
            if (StringLen(indexSeparator) > 0)
            {
                result += indexSeparator + " ";
            }
        }

        //
        string iItemString = (string)buffer[i];

        //
        result += iItemString;

        //
        if (i < bufferSize - 1)
        {
            result += separator;
        }
    }

    //
    return result;
}

/**
 * Check a String Value is Boolean or Not ...
 *
 * @param  value: string representation of value ...
 *
 * @return ( bool )
 */
bool IsBoolean(string value)
{
    //
    bool result = false;

    //
    string istring = value;
    string normalizedValue = ToXString(istring);
    StringToLower(normalizedValue);
    normalizedValue = Trim(normalizedValue);

    //
    result =
        !IsValid(normalizedValue)
            ? false
            : (normalizedValue == "true" ||
               normalizedValue == "false");

    //
    return result;
}

/**
 * Converts String to boolean ...
 *
 * @param  value: string, Provided Value ...
 *
 * @return ( bool )
 */
bool ToBoolean(string value)
{
    //
    bool result = false;

    //
    if (!IsBoolean(value) ||
        StringLen(value) <= 0)
    {
        return result;
    }

    //
    value = Trim(value);
    StringToLower(value);

    //
    bool isTrue = value == "true";
    bool isFalse = value == "false";

    //
    result = isTrue
                 ? true
                 : false;

    //
    return result;
}

//
// Split an String Representation of an Array ...
int SplitContent(
    string &result[],            // Hold Result ...
    string content,              // Source ...
    string separator = ",",      // Separator ...
    bool forceCleanResult = true // Clean Result ...
)
{
    //
    int mResult = 0;

    //
    if (forceCleanResult)
    {
        Clean(result);
    }

    //
    int before = ArraySize(result);

    //
    if (separator == NULL || StringLen(separator) == 0)
    {
        separator = ",";
    }

    //
    if (StringLen(content) == 0)
    {
        return mResult;
    }

    //
    StringSplit(
        content,
        StringGetCharacter(
            separator,
            0),
        result);

    //
    int after = ArraySize(result);

    //
    mResult = after - before;

    //
    return mResult;
}

//
// Extract Data from String ...
string ExtractString(
    const string source,            // Source String used for Extracting
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    string result = "";

    //
    // Validate String ...
    if (
        StringLen(source) == 0 ||
        StringLen(endString) == 0 ||
        StringLen(startString) == 0)
    {
        return result;
    }

    //
    // Find Start String Position ...
    int sPos = StringFind(
        source,
        startString);
    if (sPos < 0)
    {
        return result;
    }

    //
    // Find End String Position after Start String ...
    int searchEndFromPos = sPos + StringLen(startString);
    int ePos = StringFind(
        source,
        endString,
        searchEndFromPos);
    if (ePos < 0)
    {
        return result;
    }

    //
    // Extract Data from Source ...
    int dLength = ePos - searchEndFromPos;
    result = StringSubstr(
        source,
        searchEndFromPos,
        dLength);

    //
    return result;
}

//
// Extract an Array of Contents ...
int ExtractStrings(
    string &result[],               // Hold Result
    const string source,            // Source String used for Extracting
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    int mResult = 0;

    //
    Clean(result);

    //
    // Validate Args ...
    int sourceLength = StringLen(source);
    int startLength = StringLen(startString);
    int endLength = StringLen(endString);

    //
    int minRequiredLength = startLength + endLength;

    //
    if (
        endLength <= 0 ||
        startLength <= 0 ||
        sourceLength <= 0 ||
        sourceLength < minRequiredLength)
    {
        return mResult;
    }

    //
    // Make a Copy of Source for Manipulating ...
    string workStr = source;

    //
    // Define a Loop ...
    while (StringLen(workStr) > minRequiredLength)
    {
        //
        // Find Item ...
        string item = ExtractString(
            workStr,
            startString,
            endString);

        //
        // Breakout when couldn't find anymore item ...
        int itemLength = StringLen(item);
        if (itemLength <= 0)
        {
            break;
        }

        //
        // Prepare Item Source String for Replacing in Work String ...
        string itemSource = startString + item + endString;

        //
        // Add Item to Result Array ...
        Add(
            item,
            result);

        //
        // Replace Item Soure in Work String ...
        StringReplace(
            workStr,
            itemSource,
            "");
    }

    //
    // Calculate Size of Array ...
    mResult = ArraySize(result);

    //
    return mResult;
}

//
string ExtractSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    return ExtractString(
        surrounded,
        mToken + startString,
        endString
        //
    );
}

//
int ParseIntSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    int result = NULL;

    //
    string strContent = ExtractSurrounded(
        surrounded,
        mToken,
        startString,
        endString
        //
    );
    if (!IsValid(strContent))
    {
        return result;
    }

    //
    result = (int)strContent;

    //
    return result;
}

long ParseLongSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    long result = NULL;

    //
    string strContent = ExtractSurrounded(
        surrounded,
        mToken,
        startString,
        endString
        //
    );
    if (!IsValid(strContent))
    {
        return result;
    }

    //
    result = (long)strContent;

    //
    return result;
}

string ParseStringSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    string result = NULL;

    //
    string strContent = ExtractSurrounded(
        surrounded,
        mToken,
        startString,
        endString
        //
    );
    if (!IsValid(strContent))
    {
        return result;
    }

    //
    result = (string)strContent;

    //
    return result;
}

double ParseDoubleSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    double result = NULL;

    //
    string strContent = ExtractSurrounded(
        surrounded,
        mToken,
        startString,
        endString
        //
    );
    if (!IsValid(strContent))
    {
        return result;
    }

    //
    result = (double)strContent;

    //
    return result;
}

datetime ParseTimeSurrounded(
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")"    // Where to End Extraction, usually a Sign
)
{
    //
    datetime result = NULL;

    //
    string strContent = ExtractSurrounded(
        surrounded,
        mToken,
        startString,
        endString
        //
    );
    if (!IsValid(strContent))
    {
        return result;
    }

    //
    result = (datetime)strContent;

    //
    return result;
}

//
int ParseIntArraySurrounded(
    int &result[],                  // Holds Result
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    string separator = ",",         // Separator ...
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")",   // Where to End Extraction, usually a Sign
    bool forceClean = true          // Clean Result ...
)
{
    //
    int count = 0;

    //
    if (forceClean)
    {
        Clean(result);
    }

    //
    int before = ArraySize(result);

    //
    string contentStr = ParseStringSurrounded(surrounded, mToken);
    if (!IsValid(contentStr))
    {
        return count;
    }

    //
    string tmps[];
    int tmpsCount = SplitContent(
        tmps,
        contentStr
        //
    );
    if (tmpsCount <= 0)
    {
        return count;
    }

    //
    for (int i = 0; i < tmpsCount; i++)
    {
        //
        string iTMP = tmps[i];
        if (!IsValid(iTMP))
        {
            continue;
        }

        //
        int iValue = (int)iTMP;

        //
        Add(
            iValue,
            result
            //
        );
    }

    //
    int after = ArraySize(result);

    //
    count = after - before;

    //
    return count;
}

int ParseLongArraySurrounded(
    long &result[],                 // Holds Result
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    string separator = ",",         // Separator ...
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")",   // Where to End Extraction, usually a Sign
    bool forceClean = true          // Clean Result ...
)
{
    //
    int count = 0;

    //
    if (forceClean)
    {
        Clean(result);
    }

    //
    int before = ArraySize(result);

    //
    string contentStr = ParseStringSurrounded(surrounded, mToken);
    if (!IsValid(contentStr))
    {
        return count;
    }

    //
    string tmps[];
    int tmpsCount = SplitContent(
        tmps,
        contentStr
        //
    );
    if (tmpsCount <= 0)
    {
        return count;
    }

    //
    for (int i = 0; i < tmpsCount; i++)
    {
        //
        string iTMP = tmps[i];
        if (!IsValid(iTMP))
        {
            continue;
        }

        //
        long iValue = (long)iTMP;

        //
        Add(
            iValue,
            result
            //
        );
    }

    //
    int after = ArraySize(result);

    //
    count = after - before;

    //
    return count;
}

int ParseDoubleArraySurrounded(
    double &result[],               // Holds Result
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    string separator = ",",         // Separator ...
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")",   // Where to End Extraction, usually a Sign
    bool forceClean = true          // Clean Result ...
)
{
    //
    int count = 0;

    //
    if (forceClean)
    {
        Clean(result);
    }

    //
    int before = ArraySize(result);

    //
    string contentStr = ParseStringSurrounded(surrounded, mToken);
    if (!IsValid(contentStr))
    {
        return count;
    }

    //
    string tmps[];
    int tmpsCount = SplitContent(
        tmps,
        contentStr
        //
    );
    if (tmpsCount <= 0)
    {
        return count;
    }

    //
    for (int i = 0; i < tmpsCount; i++)
    {
        //
        string iTMP = tmps[i];
        if (!IsValid(iTMP))
        {
            continue;
        }

        //
        double iValue = (double)iTMP;

        //
        Add(
            iValue,
            result
            //
        );
    }

    //
    int after = ArraySize(result);

    //
    count = after - before;

    //
    return count;
}

int ParseTimeArraySurrounded(
    datetime &result[],             // Holds Result
    const string surrounded,        //  Surrounded Content
    const string mToken,            // Specified Token
    string separator = ",",         // Separator ...
    const string startString = "(", // Expression Start String must be Unique
    const string endString = ")",   // Where to End Extraction, usually a Sign
    bool forceClean = true          // Clean Result ...
)
{
    //
    int count = 0;

    //
    if (forceClean)
    {
        Clean(result);
    }

    //
    int before = ArraySize(result);

    //
    string contentStr = ParseStringSurrounded(surrounded, mToken);
    if (!IsValid(contentStr))
    {
        return count;
    }

    //
    string tmps[];
    int tmpsCount = SplitContent(
        tmps,
        contentStr
        //
    );
    if (tmpsCount <= 0)
    {
        return count;
    }

    //
    for (int i = 0; i < tmpsCount; i++)
    {
        //
        string iTMP = tmps[i];
        if (!IsValid(iTMP))
        {
            continue;
        }

        //
        datetime iValue = (datetime)iTMP;

        //
        Add(
            iValue,
            result
            //
        );
    }

    //
    int after = ArraySize(result);

    //
    count = after - before;

    //
    return count;
}

//
// Surround an String by Specific Token ...
template <typename T>
string Surround(
    string mToken,            // an String which used to Tokenize
    T &value,                 // a Value for Tokenize it
    string startString = "(", // Token Start Content
    string endString = ")"    // Token End Content
)
{
    //
    string result = NULL;

    //
    string strValue = ToXString(value);
    if (!IsValid(strValue))
    {
        return result;
    }

    //
    result =
        //
        (IsValid(mToken)
             ? mToken
             : "") +
        //
        startString +
        strValue +
        endString
        //
        ;

    //
    return result;
}

//
template <typename T>
string SurroundArray(
    string mToken,
    T &value[],
    string startString = "(", // Token Start Content
    string endString = ")"    // Token End Content
)
{
    //
    string result = NULL;

    //
    string valueStr = ToXString(value);
    if (!IsValid(valueStr))
    {
        return result;
    }

    //
    result = Surround(
        mToken,
        valueStr,
        startString,
        endString
        //
    );

    //
    return result;
}

//
// END String ...
//

//
// START Cycles ...
//

/**
 * Converts String To Period ...
 *
 * @param  value: string ...
 *
 * @return ( ENUM_TIMEFRAMES )
 */
ENUM_TIMEFRAMES ToPeriod(string value)
{
    //
    if (StringLen(value) == 0)
    {
        return _Period;
    }

    //
    StringTrimLeft(value);
    StringTrimRight(value);

    //
    ENUM_TIMEFRAMES result = _Period;

    //
    if (value == "M1")
    {
        result = PERIOD_M1;
    }
    else if (value == "M2")
    {
        result = PERIOD_M2;
    }
    else if (value == "M3")
    {
        result = PERIOD_M3;
    }
    else if (value == "M4")
    {
        result = PERIOD_M4;
    }
    else if (value == "M5")
    {
        result = PERIOD_M5;
    }
    else if (value == "M6")
    {
        result = PERIOD_M6;
    }
    else if (value == "M10")
    {
        result = PERIOD_M10;
    }
    else if (value == "M12")
    {
        result = PERIOD_M12;
    }
    else if (value == "M15")
    {
        result = PERIOD_M15;
    }
    else if (value == "M20")
    {
        result = PERIOD_M20;
    }
    else if (value == "M30")
    {
        result = PERIOD_M30;
    }
    else if (value == "H1")
    {
        result = PERIOD_H1;
    }
    else if (value == "H2")
    {
        result = PERIOD_H2;
    }
    else if (value == "H3")
    {
        result = PERIOD_H3;
    }
    else if (value == "H4")
    {
        result = PERIOD_H4;
    }
    else if (value == "H6")
    {
        result = PERIOD_H6;
    }
    else if (value == "H8")
    {
        result = PERIOD_H8;
    }
    else if (value == "H12")
    {
        result = PERIOD_H12;
    }
    else if (value == "D1")
    {
        result = PERIOD_D1;
    }
    else if (value == "W1")
    {
        result = PERIOD_W1;
    }
    else if (value == "MN1")
    {
        result = PERIOD_MN1;
    }

    //
    return result;
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_TIMEFRAMES member ...
 *
 * @return ( string )
 */
string ToXString(ENUM_TIMEFRAMES value)
{
    //
    string result = "";

    //
    string mPStr = EnumToString(value);

    string mPStrParts[];
    int partsCount = StringSplit(
        mPStr,
        StringGetCharacter("_", 0),
        mPStrParts);
    if (partsCount <= 0)
    {
        return result;
    }

    //
    result = mPStrParts[1];

    //
    return result;
}

//
// Retrieve All Available Period as an Array ...s
int GetAllAvailablePeriods(
    ENUM_TIMEFRAMES &result[] // Holds Result ...
)
{
    //
    Clean(result);

    //
    Add(
        PERIOD_M1,
        result
        //
    );

    //
    Add(
        PERIOD_M2,
        result
        //
    );

    //
    Add(
        PERIOD_M3,
        result
        //
    );

    //
    Add(
        PERIOD_M4,
        result
        //
    );

    //
    Add(
        PERIOD_M5,
        result
        //
    );

    //
    Add(
        PERIOD_M6,
        result
        //
    );

    //
    Add(
        PERIOD_M10,
        result
        //
    );

    //
    Add(
        PERIOD_M12,
        result
        //
    );

    //
    Add(
        PERIOD_M15,
        result
        //
    );

    //
    Add(
        PERIOD_M20,
        result
        //
    );

    //
    Add(
        PERIOD_M30,
        result
        //
    );

    //
    Add(
        PERIOD_H1,
        result
        //
    );

    //
    Add(
        PERIOD_H2,
        result
        //
    );

    //
    Add(
        PERIOD_H3,
        result
        //
    );

    //
    Add(
        PERIOD_H4,
        result
        //
    );

    //
    Add(
        PERIOD_H6,
        result
        //
    );

    //
    Add(
        PERIOD_H8,
        result
        //
    );

    //
    Add(
        PERIOD_H12,
        result
        //
    );

    //
    Add(
        PERIOD_D1,
        result
        //
    );

    //
    Add(
        PERIOD_W1,
        result
        //
    );

    //
    Add(
        PERIOD_MN1,
        result
        //
    );

    //
    int mResult = ArraySize(result);

    //
    return mResult;
}

//
// Get Specific Time Frame Start Candle Time ...
datetime GetPeriodStartTime(
    string mSymbol = NULL,          // Specified Symbol ...
    ENUM_TIMEFRAMES mPeriod = NULL, // Specified Period ...
    datetime mTime = NULL           // Specified Bar Time of Host Period ...
)
{
    //
    // Normalize Arg ...
    mTime = NormalizeTime(mTime);
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    int barIndex = iBarShift(
        mSymbol,
        mPeriod,
        mTime,
        false //
    );

    //
    datetime result = GetBarTime(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    return result;
}

//
// Retrieve Nearest Period base on Given Period ...
ENUM_TIMEFRAMES GetNearestPeriod(ENUM_TIMEFRAMES mPeriod)
{
    //
    ENUM_TIMEFRAMES result = _Period;

    //
    mPeriod = NormalizePeriod(mPeriod);

    //
    switch (mPeriod)
    {
        //
    case PERIOD_M1:
    case PERIOD_M2:
    case PERIOD_M3:
        result = PERIOD_M5;
        break;

    //
    case PERIOD_M4:
    case PERIOD_M5:
    case PERIOD_M6:
        result = PERIOD_M10;
        break;

        //
    case PERIOD_M10:
    case PERIOD_M12:
    case PERIOD_M15:
        result = PERIOD_M20;
        break;

        //
    case PERIOD_M20:
    case PERIOD_M30:
        result = PERIOD_H1;
        break;

    //
    case PERIOD_H1:
    case PERIOD_H2:
        result = PERIOD_H3;
        break;

    //
    case PERIOD_H3:
        result = PERIOD_H4;
        break;

    //
    case PERIOD_H4:
        result = PERIOD_H6;
        break;

    //
    case PERIOD_H6:
        result = PERIOD_H8;
        break;

    //
    case PERIOD_H8:
        result = PERIOD_H12;
        break;

    //
    default:
        result = mPeriod;
        break;
    }

    //
    return result;
}

//
// Retrieve Mediest Period base on Given Period ...
ENUM_TIMEFRAMES GetMediestPeriod(ENUM_TIMEFRAMES mPeriod)
{
    //
    ENUM_TIMEFRAMES result = _Period;

    //
    mPeriod = NormalizePeriod(mPeriod);

    //
    switch (mPeriod)
    {
        //
    case PERIOD_M1:
    case PERIOD_M2:
    case PERIOD_M3:
        result = PERIOD_M10;
        break;

    //
    case PERIOD_M4:
    case PERIOD_M5:
    case PERIOD_M6:
        result = PERIOD_M15;
        break;

        //
    case PERIOD_M10:
    case PERIOD_M12:
    case PERIOD_M15:
        result = PERIOD_M30;
        break;

        //
    case PERIOD_M20:
    case PERIOD_M30:
        result = PERIOD_H2;
        break;

    //
    case PERIOD_H1:
    case PERIOD_H2:
        result = PERIOD_H4;
        break;

    //
    case PERIOD_H3:
        result = PERIOD_H6;
        break;

    //
    case PERIOD_H4:
        result = PERIOD_H8;
        break;

    //
    case PERIOD_H6:
        result = PERIOD_H12;
        break;

    //
    case PERIOD_H8:
        result = PERIOD_D1;
        break;

    //
    default:
        result = mPeriod;
        break;
    }

    //
    return result;
}

//
// Retrieve Longest Period base on Given Period ...
ENUM_TIMEFRAMES GetLongestPeriod(ENUM_TIMEFRAMES mPeriod)
{
    //
    ENUM_TIMEFRAMES result = _Period;

    //
    mPeriod = NormalizePeriod(mPeriod);

    //
    switch (mPeriod)
    {
        //
    case PERIOD_M1:
    case PERIOD_M2:
    case PERIOD_M3:
        result = PERIOD_M30;
        break;

    //
    case PERIOD_M4:
    case PERIOD_M5:
    case PERIOD_M6:
        result = PERIOD_H1;
        break;

        //
    case PERIOD_M10:
    case PERIOD_M12:
    case PERIOD_M15:
        result = PERIOD_H2;
        break;

        //
    case PERIOD_M20:
    case PERIOD_M30:
        result = PERIOD_H4;
        break;

    //
    case PERIOD_H1:
    case PERIOD_H2:
        result = PERIOD_H6;
        break;

    //
    case PERIOD_H3:
        result = PERIOD_H8;
        break;

    //
    case PERIOD_H4:
        result = PERIOD_H12;
        break;

    //
    case PERIOD_H6:
        result = PERIOD_D1;
        break;

    //
    case PERIOD_H8:
        result = PERIOD_W1;
        break;

    //
    default:
        result = mPeriod;
        break;
    }

    //
    return result;
}

//
// Retrieve Hindmost Period base on Given Period ...
ENUM_TIMEFRAMES GetHindMostPeriod(ENUM_TIMEFRAMES mPeriod)
{
    //
    ENUM_TIMEFRAMES result = _Period;

    //
    mPeriod = NormalizePeriod(mPeriod);

    //
    switch (mPeriod)
    {
        //
    case PERIOD_M1:
    case PERIOD_M2:
    case PERIOD_M3:
        result = PERIOD_H1;
        break;

    //
    case PERIOD_M4:
    case PERIOD_M5:
    case PERIOD_M6:
        result = PERIOD_H2;
        break;

        //
    case PERIOD_M10:
    case PERIOD_M12:
    case PERIOD_M15:
        result = PERIOD_H4;
        break;

        //
    case PERIOD_M20:
    case PERIOD_M30:
        result = PERIOD_H6;
        break;

    //
    case PERIOD_H1:
    case PERIOD_H2:
        result = PERIOD_H8;
        break;

    //
    case PERIOD_H3:
        result = PERIOD_H12;
        break;

    //
    case PERIOD_H4:
        result = PERIOD_D1;
        break;

    //
    case PERIOD_H6:
        result = PERIOD_W1;
        break;

    //
    case PERIOD_H8:
        result = PERIOD_MN1;
        break;

    //
    default:
        result = mPeriod;
        break;
    }

    //
    return result;
}

//
// Retrieve Specified Cycles Period related to Host Period ...
ENUM_TIMEFRAMES GetCyclePeriod(
    ENUM_X_MARKET_CYCLES mCycle = NULL, // Specified Cycle
    ENUM_TIMEFRAMES mPeriod = NULL      // Host Period
)
{
    //
    ENUM_TIMEFRAMES result = NULL;

    //
    mPeriod = NormalizePeriod(mPeriod);
    mCycle = NormalizeCycle(mCycle);

    //
    switch (mCycle)
    {
    //
    // Short ...
    case X_MARKET_CYCLE_SHORT:
        result = GetNearestPeriod(mPeriod);
        break;

    //
    // Medium ...
    case X_MARKET_CYCLE_MEDIUM:
        result = GetMediestPeriod(mPeriod);
        break;

    //
    // Long ...
    case X_MARKET_CYCLE_LONG:
        result = GetLongestPeriod(mPeriod);
        break;

    //
    // Hind ...
    case X_MARKET_CYCLE_HIND:
        result = GetHindMostPeriod(mPeriod);
        break;
    }

    //
    return result;
}

//
// Calculate a Cycle Length based on Host Period ...
int GetCycleLengthOn(
    ENUM_TIMEFRAMES _hostPeriod,
    ENUM_X_MARKET_CYCLES _cycle,
    ENUM_X_PERIOD_METHOD _method,
    ENUM_TIMEFRAMES _period //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool isValid =
        //
        IsValid(_hostPeriod) &&
        IsValid(_method, _Period)
        //
        ;
    if (!isValid)
    {
        return result;
    }

    //
    // Calculate Period ...
    isValid =
        _method != X_PERIOD_NONE &&
        _cycle != X_MARKET_CYCLE_UNKNOWN;
    if (!isValid)
    {
        return result;
    }

    //
    if (_method == X_PERIOD_AUTO)
    {
        //
        _period = GetCyclePeriod(
            _cycle,
            _hostPeriod //
        );
    }
    isValid = IsValid(_period);
    if (!isValid)
    {
        return result;
    }

    //
    int _hostPeriodSeconds = PeriodSeconds(_hostPeriod);
    int _periodSeconds = PeriodSeconds(_period);

    //
    isValid = _periodSeconds >= _hostPeriodSeconds;
    if (!isValid)
    {
        return result;
    }

    //
    result = _periodSeconds / _hostPeriodSeconds;

    //
    return result;
}

/**
 * Retrieve Sharp Change Info for Specified Period ...
 *
 * @param  priceChange: double, required Price Change ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Specified Period ...
 *
 * @return ( int )
 */
int GetPeriodSharpData(
    double &priceChange,
    ENUM_TIMEFRAMES mPeriod = NULL //
)
{
    //
    int result = 0;

    //
    priceChange = 0.0;

    //
    mPeriod = NormalizePeriod(mPeriod);

    //
    switch (mPeriod)
    {
        //
    case PERIOD_M1:
        //
        priceChange = 0.000006;
        result = 14;
        break;

    //
    case PERIOD_M2:
        //
        priceChange = 0.000006;
        result = 14;
        break;

    //
    case PERIOD_M3:
        //
        priceChange = 0.00007;
        result = 14;
        break;

    //
    case PERIOD_M4:
        //
        priceChange = 0.000007;
        result = 14;
        break;

    //
    case PERIOD_M5:
        //
        priceChange = 0.000009;
        result = 14;
        break;

    //
    case PERIOD_M6:
        //
        priceChange = 0.000009;
        result = 14;
        break;

    //
    case PERIOD_M10:
        //
        priceChange = 0.00001;
        result = 12;
        break;

    //
    case PERIOD_M12:
        //
        priceChange = 0.000011;
        result = 11;
        break;

    //
    case PERIOD_M15:
        //
        priceChange = 0.000011;
        result = 9;
        break;

    //
    case PERIOD_M20:
        //
        priceChange = 0.000012;
        result = 9;
        break;

    //
    case PERIOD_M30:
        //
        priceChange = 0.000012;
        result = 7;
        break;

    //
    case PERIOD_H1:
        //
        priceChange = 0.000013;
        result = 5;
        break;

    //
    case PERIOD_H2:
        //
        priceChange = 0.000013;
        result = 5;
        break;

    //
    case PERIOD_H3:
        //
        priceChange = 0.000014;
        result = 5;
        break;

    //
    case PERIOD_H4:
        //
        priceChange = 0.000014;
        result = 5;
        break;

    //
    case PERIOD_H6:
        //
        priceChange = 0.000015;
        result = 5;
        break;

    //
    case PERIOD_H8:
        //
        priceChange = 0.000017;
        result = 5;
        break;

        //
    case PERIOD_H12:
        //
        priceChange = 0.000017;
        result = 5;
        break;

    //
    case PERIOD_D1:
        //
        priceChange = 0.000019;
        result = 3;
        break;

    //
    case PERIOD_W1:
        //
        priceChange = 0.00002;
        result = 3;
        break;

    //
    case PERIOD_MN1:
        //
        priceChange = 0.000021;
        result = 3;
        break;
    }

    //
    return result;
}

//
// END Cycles ...
//

//
// START Collections ...
//

//
// Add Specified Item to Array ...
template <typename T>
int Add(
    T item,     // item want to add
    T &buffer[] // Destination buffer
)
{
    //
    int result = 0;

    //
    ArrayResize(
        buffer,
        ArraySize(buffer) + 1);

    //
    buffer[ArraySize(buffer) - 1] = item;

    //
    result = ArraySize(buffer);

    //
    return result;
}
template <typename T>
int AddSpecified(
    T item,     // item want to add
    T &buffer[] // Destination buffer
)
{
    //
    return Add(
        item,
        buffer //
    );
}
template <typename T>
int AddRef(
    T &item,    // item want to add
    T &buffer[] // Destination buffer
)
{
    //
    int result = 0;

    //
    ArrayResize(
        buffer,
        ArraySize(buffer) + 1);

    //
    buffer[ArraySize(buffer) - 1] = item;

    //
    result = ArraySize(buffer);

    //
    return result;
}

//
// Check Specified Array Has Childs or not ...
template <typename T>
bool HasChild(T &buffer[])
{
    return IsValidSize(ArraySize(buffer));
}
template <typename T>
bool SpecifiedHasChild(T &buffer[])
{
    return HasChild(buffer);
}

//
// Remove Specified Item from an Array ...
template <typename T>
bool Remove(
    T item,
    T &buffer[] //
)
{
    //
    bool result = false;

    //
    int itemIndex = FindIndex(
        item,
        buffer);
    if (itemIndex < 0)
    {
        return result;
    }

    //
    result = ArrayRemove(
        buffer,
        itemIndex,
        1);

    //
    return result;
}

//
// Clean Specified Array ...
template <typename T>
void Clean(T &buffer[])
{
    //
    if (!HasChild(buffer))
    {
        return;
    }

    //
    // ArrayResize(buffer, 0);
    ArrayFree(buffer);
    ZeroMemory(buffer);
}
template <typename T>
void SpecifiedClean(T &buffer[])
{
    Clean(buffer);
}
template <typename T>
void XClean(T &buffer[])
{
    Clean(buffer);
}

//
// Copy Whole Content of Source to Dest array ...
template <typename T>
void Copy(
    T &source[],          // Source Buffer ...
    T &dest[],            // Dest Buffer ...
    bool cleanDest = true // Force Clen Dest Buffer ...
)
{
    //
    if (cleanDest)
    {
        Clean(dest);
    }

    //
    int sourceCount = ArraySize(source);
    if (sourceCount <= 0)
    {
        return;
    }

    //
    for (int i = 0; i < sourceCount; i++)
    {
        //
        T iSource = source[i];

        //
        AddRef(
            iSource,
            dest);
    }
}

//
// Copy Items from a Buffer ...
template <typename T>
int Copy(
    int start,              // Start
    int count,              // Number of Items for read
    T &source[],            // Source Buffer
    T &dest[],              // Dest Buffer
    bool forceClean = true, // Force To Clean buffer
    bool forceStart = true  // Force To Clean buffer
)
{
    //
    int result = 0;

    //
    if (forceClean)
    {
        Clean(dest);
    }

    //
    if (start < 0 && forceStart)
    {
        start = 0;
    }

    //
    if (start >= ArraySize(source))
    {
        start = ArraySize(source) - 1;
    }

    //
    if (count == 0)
    {
        count = ArraySize(source) - 1 - start;
    }

    //
    if (start + count > ArraySize(source))
    {
        return result;
    }

    //
    int beforeSize = ArraySize(dest);

    //
    bool asSeriesDest = ArrayGetAsSeries(dest);
    bool asSeriesSource = ArrayGetAsSeries(source);

    //
    ArraySetAsSeries(dest, true);
    ArraySetAsSeries(source, true);

    //
    ArrayCopy(
        dest,
        source,
        0,
        start,
        count
        //
    );

    //
    int afterSize = ArraySize(dest);

    //
    result = afterSize - beforeSize;

    //
    ArraySetAsSeries(dest, asSeriesDest);
    ArraySetAsSeries(source, asSeriesSource);

    //
    return result;
}

//
// Copy Items from a Buffer ...
template <typename T>
int CopyRef(
    int start,             // Start
    int count,             // Number of Items for read
    T &source[],           // Source Buffer
    T &dest[],             // Dest Buffer
    bool forceClean = true // Force To Clean buffer
)
{
    //
    int result = 0;

    //
    if (forceClean)
    {
        Clean(dest);
    }

    //
    if (start < 0)
    {
        start = 0;
    }

    //
    if (start >= ArraySize(source))
    {
        start = ArraySize(source) - 1;
    }

    //
    int beforeSize = ArraySize(dest);

    //
    bool asSeriesDest = ArrayGetAsSeries(dest);
    bool asSeriesSource = ArrayGetAsSeries(source);

    //
    ArraySetAsSeries(dest, true);
    ArraySetAsSeries(source, true);

    //
    for (int i = start; i < start + count; i++)
    {
        //
        AddRef(
            source[i],
            dest
            //
        );
    }

    //
    int afterSize = ArraySize(dest);

    //
    result = afterSize - beforeSize;

    //
    ArraySetAsSeries(dest, asSeriesDest);
    ArraySetAsSeries(source, asSeriesSource);

    //
    return result;
}

//
// Retrive Last Item of Specified Buffer ...
template <typename T>
bool GetLastItem(
    T &item,    // Holds Result
    T &buffer[] // Specified Buffer
)
{
    //
    bool result = false;

    //
    int bufferSize = ArraySize(buffer);
    if (bufferSize <= 0)
    {
        return result;
    }

    //
    item = buffer[bufferSize - 1];

    //
    result = true;
    return result;
}

//
// Remove Last Item of Specified Buffer ...
template <typename T>
bool RemoveLastItem(
    T &buffer[] // Specified Buffer
)
{
    //
    bool result = false;

    //
    int count = ArraySize(buffer);
    result = IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    ArrayRemove(
        buffer,
        count - 1,
        1 //
    );

    //
    return result;
}

template <typename T>
void CleanupArray(
    T &buffer[],       // Buffer to Cleanup ...
    int maxAllowed = 0 // Max Allowed Number of items ...
)
{
    //
    if (maxAllowed <= 0)
    {
        return;
    }

    //
    int count = ArraySize(buffer);
    if (!IsValidSize(count) ||
        count <= maxAllowed)
    {
        return;
    }

    //
    int mustRemove = count - maxAllowed;
    if (!IsValidSize(mustRemove))
    {
        return;
    }

    //
    ArrayRemove(
        buffer,
        0,
        mustRemove //
    );
}

template <typename T>
void CleanupArray(
    int &removedIndexes[],
    T &buffer[] // Buffer to Cleanup ...
)
{
    //
    bool isValid = HasChild(buffer) &&
                   HasChild(removedIndexes);
    if (!isValid)
    {
        return;
    }

    //
    for (int i = 0; i < ArraySize(removedIndexes); i++)
    {
        //
        int idx = removedIndexes[i];
        if (IsValidIndex(idx) && ArraySize(buffer) > idx)
        {
            //
            ArrayRemove(
                buffer,
                idx,
                1 //
            );
        }
    }

    //
    Clean(removedIndexes);
}

//
// Search Array For Specific Item ...
template <typename T>
int FindIndex(
    T item,     // What is Search ...
    T &buffer[] // Search in ...
)
{
    //
    int result = -1;

    //
    int count = ArraySize(buffer);
    if (count <= 0)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        T iT = buffer[i];

        //
        if (iT == item)
        {
            //
            result = i;
            break;
        }
    }

    //
    return result;
}
template <typename T>
int FindXIndex(
    T item,     // What is Search ...
    T &buffer[] // Search in ...
)
{
    return FindIndex(item, buffer);
}

//
// Find Specific Item Index inside a Collection ...
template <typename T>
int FindIndex(
    T &item,         // Item to Find
    const T &items[] // Items for Search
)
{
    //
    int result = -1;

    //
    // Check Item Validation ...
    if (!item.IsValid())
    {
        return result;
    }

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    for (int i = 0; i < itemsCount; i++)
    {
        //
        T iItem = items[i];

        //
        // Validate Item ...
        if (!iItem.IsValid())
        {
            continue;
        }

        //
        bool isSame = item.IsSameAs(iItem);
        if (isSame && result <= -1)
        {
            //
            result = i;
            break;
        }
    }

    //
    return result;
}
template <typename T>
int FindXIndex(
    T &item,         // Item to Find
    const T &items[] // Items for Search
)
{
    return FindIndex(item, items);
}

//
// Check an Array Contains Specified Value ...
template <typename T>
bool Contains(
    T item,     // What is Search ...
    T &buffer[] // Search in ...
)
{
    //
    bool result = false;

    //
    int idx = FindIndex(
        item,
        buffer);
    result = idx > -1;

    //
    return result;
}

//
// Calculate a Buffer's Sum ...
template <typename T>
double GetSum(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    double result = 0;

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Calculate Summary ...
    for (int i = 0; i < tmpCount; i++)
    {
        result += buffer[i];
    }

    //
    return result;
}

//
// Calculate a Buffer's Average ...
template <typename T>
double GetAverage(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    double result = 0;

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Calculate Summary ...
    for (int i = 0; i < tmpCount; i++)
    {
        result += buffer[i];
    }

    //
    // Calculate Average ...
    result = result / tmpCount;

    //
    return result;
}

//
// Get Max of Specific Loopback of a Buffer ...
template <typename T>
T GetMax(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    T result = 0;

    //
    if (count <= 0)
    {
        count = ArraySize(buffer);
    }

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Loop Through LoopBack ...
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        result =
            //
            result == 0 || result < iValue
                //
                ? iValue
                //
                : result
            //
            ;
    }

    //
    return result;
}
template <typename T>
T GetMaxWidthIndex(
    T &buffer[],   //  the Buffer which required to search
    int &index,    // Item Index ...
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    T result = 0;
    index = -1;

    //
    if (count <= 0)
    {
        count = ArraySize(buffer);
    }

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Loop Through LoopBack ...
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        bool isNew = result == 0;
        bool canSet = isNew ||
                      (!isNew &&
                       result < iValue);
        if (canSet)
        {
            //
            index = i;
            result = iValue;
        }
    }

    //
    return result;
}
template <typename T>
T GetSpecifiedMax(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    return GetMax(
        buffer,
        start,
        count //
    );
}
template <typename T>
T GetSpecifiedMaxWidthIndex(
    T &buffer[],   //  the Buffer which required to search
    int &index,    // Item Index ...
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    return GetMaxWidthIndex(
        buffer,
        index,
        start,
        count //
    );
}

//
// Get Min of Specific Loopback of a Buffer ...
template <typename T>
T GetMin(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    T result = 0;

    //
    if (count <= 0)
    {
        count = ArraySize(buffer);
    }

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Loop Through LoopBack ...
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        result =
            //
            result == 0 || result > iValue
                //
                ? iValue
                //
                : result
            //
            ;
    }

    //
    return result;
}
template <typename T>
T GetMinWidthIndex(
    T &buffer[],   //  the Buffer which required to search
    int &index,    // Item Index ...
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    //
    T result = 0;
    index = -1;

    //
    if (count <= 0)
    {
        count = ArraySize(buffer);
    }

    //
    T tmp[];
    int tmpCount = Copy(
        start,
        count,
        buffer,
        tmp
        //
    );
    if (tmpCount <= 0)
    {
        return result;
    }

    //
    // Loop Through LoopBack ...
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        bool isNew = result == 0;
        bool canSet = isNew ||
                      (!isNew &&
                       result > iValue);
        if (canSet)
        {
            //
            index = i;
            result = iValue;
        }
    }

    //
    return result;
}
template <typename T>
T GetSpecifiedMin(
    T &buffer[],   //  the Buffer which required to search
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    return GetMin(
        buffer,
        start,
        count //
    );
}
template <typename T>
T GetSpecifiedMinWidthIndex(
    T &buffer[],   //  the Buffer which required to search
    int &index,    // Item Index ...
    int start = 0, // Start Index
    int count = 0  // Count for Search
)
{
    return GetMinWidthIndex(
        buffer,
        index,
        start,
        count //
    );
}

//
// Find a Value less than Specified ...
template <typename T>
T FindLesserThan(
    T value,               // Specified Value ...
    const T &source[],     // Source ...
    bool isDescend = false // Find Biggest Lesser Value ...
)
{
    //
    T result = -1;

    //
    int itemsCount = ArraySize(source);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    // Make a Copy of source ...
    T tmp[];

    //
    ArrayResize(
        tmp,
        ArraySize(source) //
    );

    //
    ArrayCopy(
        tmp,
        source //
    );

    //
    // Sort Temp Buffer ...
    ArraySort(tmp);
    if (isDescend)
    {
        ArrayReverse(tmp);
    }

    //
    int tmpCount = ArraySize(tmp);
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        if (iValue < value)
        {
            //
            result = iValue;
            break;
        }
    }

    //
    return result;
}
template <typename T>
T FindLesserThanByIndex(
    T value,               // Specified Value ...
    int &index,            // Item Index ...
    const T &source[],     // Source ...
    bool isDescend = false // Find Biggest Lesser Value ...
)
{
    //
    T result = -1;
    index = -1;

    //
    int itemsCount = ArraySize(source);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    // Make a Copy of source ...
    T tmp[];

    //
    ArrayResize(
        tmp,
        ArraySize(source) //
    );

    //
    ArrayCopy(
        tmp,
        source //
    );

    //
    // Sort Temp Buffer ...
    ArraySort(tmp);
    if (isDescend)
    {
        ArrayReverse(tmp);
    }

    //
    int tmpCount = ArraySize(tmp);
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        if (iValue < value)
        {
            //
            index = i;
            result = iValue;
            break;
        }
    }

    //
    return result;
}

//
// Find a Value less than Specified ...
template <typename T>
T FindBiggerThan(
    T value,               // Specified Value ...
    const T &source[],     // Source ...
    bool isDescend = false // Find Smallest Bigger Value ...
)
{
    //
    T result = -1;

    //
    int itemsCount = ArraySize(source);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    // Make a Copy of source ...
    T tmp[];

    //
    ArrayResize(
        tmp,
        ArraySize(source) //
    );

    //
    ArrayCopy(
        tmp,
        source //
    );

    //
    // Sort Temp Buffer ...
    ArraySort(tmp);
    if (!isDescend)
    {
        ArrayReverse(tmp);
    }

    //
    int tmpCount = ArraySize(tmp);
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        if (iValue > value)
        {
            //
            result = iValue;
            break;
        }
    }

    //
    return result;
}
template <typename T>
T FindBiggerThanByIndex(
    T value,               // Specified Value ...
    int &index,            // Item Index ...
    const T &source[],     // Source ...
    bool isDescend = false // Find Smallest Bigger Value ...
)
{
    //
    T result = -1;
    index = -1;

    //
    int itemsCount = ArraySize(source);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    // Make a Copy of source ...
    T tmp[];

    //
    ArrayResize(
        tmp,
        ArraySize(source) //
    );

    //
    ArrayCopy(
        tmp,
        source //
    );

    //
    // Sort Temp Buffer ...
    ArraySort(tmp);
    if (!isDescend)
    {
        ArrayReverse(tmp);
    }

    //
    int tmpCount = ArraySize(tmp);
    for (int i = 0; i < tmpCount; i++)
    {
        //
        T iValue = tmp[i];

        //
        if (iValue > value)
        {
            //
            index = i;
            result = iValue;
            break;
        }
    }

    //
    return result;
}

//
// Find Same Values in Array ...
template <typename T>
void FindSames(
    T &result[],          // Holds Result ...
    T &source[],          // Source ...
    int verifications = 3 // Number of Consequence Repeat ...
)
{
    //
    Clean(result);

    //
    // TenkanSen Flats ...
    int sourceCount = ArraySize(source);
    if (sourceCount > verifications)
    {
        //
        T mLast = 0;
        int mVerified = 0;
        for (int i = 0; i < sourceCount; i++)
        {
            //
            T iVal = source[i];
            if (mLast == 0)
            {
                mLast = iVal;
            }
            else if (mLast == iVal)
            {
                mVerified++;
            }
            else
            {
                mLast = iVal;
                mVerified = 0;
            }

            //
            if (iVal == mLast && mVerified >= verifications)
            {
                //
                Add(
                    iVal,
                    result);

                //
                mLast = 0;
                mVerified = 0;
            }
        }
    }
}

//
// Add an Item to a Buffer if not Exists ...
void AddIfNotExists(
    double value,
    double &values[],
    double smoothingPoint = 3 //
)
{
    //
    double smoothingValue = smoothingPoint * GetPoints(_Symbol);

    //
    int valuesCount = ArraySize(values);
    if (valuesCount <= 0)
    {
        //
        Add(value,
            values);
    }
    else
    {
        //
        bool isExists = false;
        for (int i = 0; i < valuesCount; i++)
        {
            //
            double iVal = values[i];

            //
            double diff = MathAbs(iVal - value);

            //
            if (iVal == value || diff < smoothingValue)
            {
                //
                isExists = true;
                break;
            }
        }

        //
        if (!isExists)
        {
            //
            Add(value,
                values);
        }
    }
}

//
// Add a Buffers Child to another Buffer
// if it's not Contains them ...
void AddsIfNotExists(
    double &source[],
    double &dest[],
    double smoothingPoint = 3 //
)
{
    //
    double smoothingValue = smoothingPoint * GetPoints(_Symbol);

    //
    int sourceCount = ArraySize(source);
    if (sourceCount <= 0)
    {
        return;
    }

    //
    for (int i = 0; i < sourceCount; i++)
    {
        //
        double iSource = source[i];

        //
        AddIfNotExists(
            iSource,
            dest,
            smoothingPoint);
    }
}

//
// END Collections ...
//

//
// START Buffers ...
//

bool IsSame(
    double current,
    double previous,
    double tolerance = 2 //
)
{
    //
    bool result =
        NotEmptyZero(current) &&
        NotEmptyZero(previous) &&
        current <= previous + tolerance &&
        current >= previous + tolerance;

    //
    return result;
}

bool IsOver(
    double current,
    double previous,
    double tolerance = 2 //
)
{
    //
    bool result =
        NotEmptyZero(current) &&
        NotEmptyZero(previous) &&
        current >= previous + tolerance;

    //
    return result;
}

bool IsUnder(
    double current,
    double previous,
    double tolerance = 2 //
)
{
    //
    bool result =
        NotEmptyZero(current) &&
        NotEmptyZero(previous) &&
        current <= previous - tolerance;

    //
    return result;
}

//
template <typename T>
bool IsSame(
    T &buffer[],   // Search Buffer
    int count = 5, // Number of Searchs
    int start = 0  // Start ...
)
{
    //
    bool result = false;

    //
    int bufferSize = ArraySize(buffer);
    result = bufferSize > 0;
    if (!result)
    {
        return result;
    }

    //
    // Validate Count ...
    result = start + count < bufferSize;
    if (!result)
    {
        return result;
    }

    //
    T iTem = buffer[start];
    for (int i = start; i < start + count; i++)
    {
        //
        if (iTem != buffer[i])
        {
            result = false;
            break;
        }

        //
        if (!result)
        {
            result = true;
        }
    }

    //
    return result;
}

//
// Check first Cross Over second at index ...
bool IsCrossedOver(
    const double &first[],  // the buffer which check crossing over second buffer
    const double &second[], // first buffer checks based on this buffer
    const int index = 0     // check crosses in specific index
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    if (ArraySize(first) < index + 1 || ArraySize(second) < index + 1)
    {
        return result;
    }

    //
    result = first[index] > second[index] &&
             !(first[index + 1] > second[index + 1]);

    //
    return result;
}

//
// Check first is Over second at index ...
bool IsOver(
    const double &first[],  // the buffer which check over second buffer
    const double &second[], // first buffer checks based on this buffer
    const int index = 0     // check crosses in specific index
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    if (ArraySize(first) < index + 1 || ArraySize(second) < index + 1)
    {
        return result;
    }

    //
    result = first[index] > second[index] &&
             first[index + 1] > second[index + 1];

    //
    return result;
}

//
// Check first Cross Under second at index ...
bool IsCrossedUnder(
    const double &first[],  // the buffer which check crossing under second buffer
    const double &second[], // first buffer checks based on this buffer
    const int index = 0     // check crosses in specific index
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    if (ArraySize(first) < index + 1 || ArraySize(second) < index + 1)
    {
        return result;
    }

    //
    result = first[index] < second[index] &&
             !(first[index + 1] < second[index + 1]);

    //
    return result;
}

//
// Check first is Under second at index ...
bool IsUnder(
    const double &first[],  // the buffer which check under second buffer
    const double &second[], // first buffer checks based on this buffer
    const int index = 0     // check crosses in specific index
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    if (ArraySize(first) < index + 1 || ArraySize(second) < index + 1)
    {
        return result;
    }

    //
    result = first[index] < second[index] &&
             first[index + 1] < second[index + 1];

    //
    return result;
}

//
// Check if a Value Increasing in Loopback ...
bool IsIncreasing(
    const double &buffer[], // the Buffer which required to search
    int from = -1,          // Last Time Index
    int to = -1             // Fisrs Tima Index
)
{
    //
    bool result = false;

    //
    if (from == -1)
    {
        from = ArraySize(buffer) - 1;
    }

    //
    if (to == -1)
    {
        to = 0;
    }

    //
    // Validate Args ...
    if (
        from <= to ||
        ArraySize(buffer) < from + 1)
    {
        return result;
    }

    //
    // Loop through Items ...
    double toValue = buffer[to];
    double fromValue = buffer[from];
    result = toValue >= fromValue;
    for (int i = to + 1; i <= from - 1; i++)
    {
        //
        double iValue = buffer[i];
        bool isPassed = toValue >= iValue;

        //
        bool isIIncreasing = IsIncreasing(
            buffer,
            from,
            i //
        );

        //
        // Check Result ...
        result = result && isPassed && isIIncreasing;
        if (!result)
        {
            break;
        }
    }

    //
    return result;
}

//
// Check if a Value Decreasing in Loopback ...
bool IsDecreasing(
    const double &buffer[], // the Buffer which required to search
    int from = -1,          // Last Time Index
    int to = -1             // Fisrs Tima Index
)
{
    //
    bool result = false;

    //
    if (from == -1)
    {
        from = ArraySize(buffer) - 1;
    }

    //
    if (to == -1)
    {
        to = 0;
    }

    //
    // Validate Args ...
    if (
        from <= to ||
        ArraySize(buffer) < from + 1)
    {
        return result;
    }

    //
    // Loop through Items ...
    double toValue = buffer[to];
    double fromValue = buffer[from];
    result = toValue <= fromValue;
    for (int i = to + 1; i <= from - 1; i++)
    {
        //
        double iValue = buffer[i];
        bool isPassed = toValue <= iValue;

        //
        bool isIDecreasing = IsDecreasing(
            buffer,
            from,
            i //
        );

        //
        // Check Result ...
        result = result && isPassed && isIDecreasing;
        if (!result)
        {
            break;
        }
    }

    //
    return result;
}

//
bool IsTrendingUp(
    const double &buffer1[], // First Buffer
    const double &buffer2[]  // Seccend Buffer
)
{
    //
    bool result = false;

    //
    int buffer1Count = ArraySize(buffer1);
    int buffer2Count = ArraySize(buffer2);
    if (buffer1Count <= 0 || buffer2Count <= 0)
    {
        return result;
    }

    //
    int count = MathMin(buffer1Count, buffer2Count);
    if (count <= 1)
    {
        return result;
    }

    //
    int upCounts = 0;
    int downCounts = 0;
    double value = MathAbs(buffer1[0] - buffer2[0]);
    for (int i = 1; i < count; i++)
    {
        //
        double iValue = MathAbs(buffer1[i] - buffer2[i]);

        //
        if (value > iValue)
        {
            upCounts++;
        }

        //
        if (value < iValue)
        {
            downCounts++;
        }
    }

    //
    result = upCounts > 1 && upCounts > downCounts;

    //
    return result;
}

//
bool IsTrendingDown(
    const double &buffer1[], // First Buffer
    const double &buffer2[]  // Seccend Buffer
)
{
    //
    bool result = false;

    //
    int buffer1Count = ArraySize(buffer1);
    int buffer2Count = ArraySize(buffer2);
    if (buffer1Count <= 0 || buffer2Count <= 0)
    {
        return result;
    }

    //
    int count = MathMin(buffer1Count, buffer2Count);
    if (count <= 1)
    {
        return result;
    }

    //
    int upCounts = 0;
    int downCounts = 0;
    double value = MathAbs(buffer1[0] - buffer2[0]);
    for (int i = 1; i < count; i++)
    {
        //
        double iValue = MathAbs(buffer1[i] - buffer2[i]);

        //
        if (value > iValue)
        {
            upCounts++;
        }

        //
        if (value < iValue)
        {
            downCounts++;
        }
    }

    //
    result = downCounts > 1 && downCounts > upCounts;

    //
    return result;
}

//
// Calculate Slope of Specified Buffer at Specified Index ...
double GetSlope(
    const double &buffer[], // Buffer
    int from,               // In Past Index ...
    int to                  // In Past Index ...
)
{
    //
    double result = 0;

    //
    if (from > ArraySize(buffer) || to > ArraySize(buffer))
    {
        return result;
    }

    //
    double toValue = buffer[to];
    double fromValue = buffer[from];
    double deltaValue = toValue - fromValue;
    double deltaTime = from - to;

    //
    result = deltaValue / deltaTime;

    //
    return result;
}

//
// END Buffers ...
//

//
// START Time ...
//

//
// Converts Time to Seconds ...
ulong TimeToSeconds(
    datetime time // Specify time to Convert ...
)
{
    //
    ulong result = (ulong)time;

    //
    return result;
}
ulong TimeToSeconds(
    MqlDateTime &time // Specify time to Convert ...
)
{
    //
    datetime dTime = StructToTime(time);

    //
    ulong result = TimeToSeconds(dTime);

    //
    return result;
}

//
// Converts Seconds To Time ...
datetime SecondsToTime(
    ulong seconds // Specify Seconds to Convert ...
)
{
    //
    datetime result = (datetime)seconds;

    //
    return result;
}
MqlDateTime SecondsToStruct(
    ulong seconds // Specify Seconds to Convert ...
)
{
    //
    datetime dTime = SecondsToTime(seconds);

    //
    MqlDateTime result = {};
    TimeToStruct(
        dTime,
        result);

    //
    return result;
}

//
// Converts Time to MilliSeconds ...
ulong TimeToMilliSeconds(
    datetime time // Specify time to Convert ...
)
{
    //
    ulong result = TimeToSeconds(time) * 1000;

    //
    return result;
}
ulong TimeToMilliSeconds(
    MqlDateTime &time // Specify time to Convert ...
)
{
    //
    datetime dTime = StructToTime(time);

    //
    ulong result = TimeToMilliSeconds(dTime);

    //
    return result;
}

//
// Converts MillisSeconds to Time ...
datetime MilliSecondsToTiem(
    ulong milliSeconds // Specify MilliSeconds to Convert ...
)
{
    //
    ulong seconds = milliSeconds / 1000;

    //
    datetime result = SecondsToTime(seconds);

    //
    return result;
}
MqlDateTime MilliSecondsToStruct(
    ulong milliSeconds // Specify MilliSeconds to Convert ...
)
{
    //
    datetime dTime = MilliSecondsToTiem(milliSeconds);

    //
    MqlDateTime result = {};
    TimeToStruct(
        dTime,
        result);

    //
    return result;
}

//
// Converts Hours to Seconds ...
int HoursToSeconds(int value)
{
    //
    int result = 0;

    //
    if (value < 0)
    {
        value = 0;
    }

    //
    if (value == 0)
    {
        return result;
    }

    //
    result = value * 60 * 60;

    //
    return result;
}

//
// Retrieve DateTime Structure ...
MqlDateTime GetCurrentTime()
{
    //
    MqlDateTime result = {};

    //
    datetime cTime = TimeCurrent();
    TimeToStruct(cTime, result);

    //
    return result;
}

//
// Retrieve Current Time as Seconds ...
ulong GetCurrentTimeAsSeconds()
{
    //
    datetime time = TimeCurrent();

    //
    ulong result = TimeToSeconds(time);

    //
    return result;
}

//
// Retrieve  Current Time as MilliSeconds ...
ulong GetCurrentTimeAsMilliSeconds()
{
    //
    datetime time = TimeCurrent();

    //
    ulong result = TimeToMilliSeconds(time);

    //
    return result;
}

//
int GetLocalOffset()
{
    //
    MqlDateTime cTimeStruct;
    datetime cTime = TimeCurrent(cTimeStruct);

    //
    MqlDateTime cLTimeStruct;
    datetime cLTime = TimeLocal(cLTimeStruct);

    //
    cTimeStruct.sec = 0;
    cTime = StructToTime(cTimeStruct);

    //
    cLTimeStruct.sec = 0;
    cLTime = StructToTime(cLTimeStruct);

    //
    int result = ((int)cLTime - (int)cTime);

    //
    return result;
}

//
int GetUTCRequiredTimeOffset()
{
    //
    int utcOffset = TimeGMTOffset();
    int localOffset = GetLocalOffset();

    //
    int result = utcOffset + localOffset;

    //
    return result;
}

//
datetime GetUTCTime(
    datetime time = NULL,
    bool applyTradeServerTime = false //
)
{
    //
    datetime result = NULL;

    //
    NormalizeTime(time);

    //
    datetime cTime = TimeCurrent();
    datetime cTimeLocal = TimeLocal();

    //
    int cLocalOffset = ((int)cTimeLocal - (int)cTime);
    int gmtOffset = TimeGMTOffset();
    int offsetDelte = gmtOffset + cLocalOffset;
    int requiredUTCOffset = GetUTCRequiredTimeOffset();
    if (requiredUTCOffset > 0)
    {
        requiredUTCOffset = -1 * requiredUTCOffset;
    }

    //
    int timeSeconds = (int)time;
    int resultSeconds = timeSeconds + requiredUTCOffset;

    //
    result = (datetime)(resultSeconds);

    //
    return result;
}

//
// Retrieve only Hour and Minute as String ...
string GetTimeString(
    datetime time = NULL //
)
{
    //
    NormalizeTime(time);

    //
    string result = NULL;

    //
    MqlDateTime tStruct;
    bool isStructed = TimeToStruct(time, tStruct);
    if (!isStructed)
    {
        return result;
    }

    //
    string hour = ToXString(tStruct.hour);
    hour =
        StringLen(hour) == 1
            ? "0" + hour
            : hour;

    //
    string minute = ToXString(tStruct.min);
    minute =
        StringLen(minute) == 1
            ? "0" + minute
            : minute;

    //
    result = hour + ":" + minute;

    //
    return result;
}

//
// Converts a Date time to File Format ...
string ToFormatString(
    datetime mTime,
    string separator = "_" //
)
{
    //
    string result = NULL;

    //
    if (!IsValid(mTime))
    {
        return result;
    }

    //
    MqlDateTime timeStruct;
    bool isConverts = TimeToStruct(
        mTime,
        timeStruct //
    );
    if (!isConverts)
    {
        return result;
    }

    //
    string year = ToXString(timeStruct.year);
    string month = ToXString(timeStruct.mon);
    string day = ToXString(timeStruct.day);
    string hour = ToXString(timeStruct.hour);
    string minute = ToXString(timeStruct.min);
    string second = ToXString(timeStruct.sec);

    //
    result =
        //
        // Year ...
        year + separator +
        //
        // Month ...
        (StringLen(month) == 1
             ? "0" + month
             : month) +
        separator +
        //
        // Day ...
        (StringLen(day) == 1
             ? "0" + day
             : day) +
        separator +
        //
        // Hour ...
        (StringLen(hour) == 1
             ? "0" + hour
             : hour) +
        separator +
        //
        // Minute ...
        (StringLen(minute) == 1
             ? "0" + minute
             : minute) +
        separator +
        //
        // Seconds ...
        (StringLen(second) == 1
             ? "0" + second
             : second) +
        separator +
        //
        ""
        //
        ;

    //
    return result;
}

/**
 * Converts an String Represetation of Time (00:00) to Date Time ..
 *
 * @param  value: String ...
 *
 * @return ( datetime )
 */
datetime ParseDateTimeFromTimeString(string value)
{
    //
    datetime result = NULL;

    //
    if (!IsValid(value))
    {
        return result;
    }

    //
    // Check Signalling Time ...
    MqlDateTime cTime;
    bool isTimeRecieved = TimeCurrent(cTime);
    if (!isTimeRecieved)
    {
        return result;
    }

    //
    // Split ...
    string parts[];
    int partsCount = SplitContent(
        parts,
        value,
        ":"
        //
    );
    if (!IsValidSize(partsCount))
    {
        return result;
    }

    //
    int hour = 0;
    int minute = 0;
    if (partsCount >= 1)
    {
        hour = (int)(parts[0]);
    }
    if (partsCount >= 2)
    {
        minute = (int)(parts[1]);
    }

    //
    cTime.day_of_week = 0;
    cTime.day_of_year = 0;

    //
    cTime.hour = hour;
    cTime.min = minute;

    //
    result = StructToTime(cTime);

    //
    return result;
}

//
// Check Specific Time in Range of another Time ...
bool IsTimeInRange(
    datetime currentTime = NULL,
    datetime startTime = NULL,
    datetime stopTime = NULL //
)
{
    //
    bool result = false;

    //
    // Normallize Current Time ...
    currentTime = NormalizeTime(currentTime);

    //
    result = !IsValid(startTime) &&
             !IsValid(stopTime);
    if (result)
    {
        return result;
    }

    //
    result = IsValid(startTime) &&
             IsValid(stopTime);
    if (!result)
    {
        //
        result = IsValid(startTime) &&
                 currentTime >= startTime;

        //
        return result;
    }

    //
    result = currentTime >= startTime &&
             currentTime < stopTime;

    //
    return result;
}
bool IsTimeInRange(
    datetime currentTime,
    string startTime,
    string endTime //
)
{
    //
    bool result = false;

    //
    result = IsValid(startTime) &&
             IsValid(endTime);
    if (!result)
    {
        //
        // Here Since there isnot provide any
        // data source we pass result as true ...
        result = true;
        return result;
    }

    //
    // Converts String Representation to Time ...
    datetime st = ParseDateTimeFromTimeString(startTime);
    datetime et = ParseDateTimeFromTimeString(endTime);

    //
    result = IsTimeInRange(
        currentTime,
        st,
        et //
    );

    //
    return result;
}

//
// END Time ...
//

//
// START Price ...
//

//
// Get 1 Pip in Price Value ...
double GetPipPrice(
    string mSymbol = NULL // Trading Symbol
)
{
    //
    mSymbol = NormalizeSymbol(mSymbol);

    //
    double symbolPoint = GetPoints(mSymbol);
    int symbolDigits = GetDigits(mSymbol);

    //
    double result = symbolPoint;
    if (symbolDigits == 3 || symbolDigits == 5)
    {
        result *= 10;
    }

    //
    return result;
}

/**
 * Calculate Applied Price ...
 *
 * @param  mType: ENUM_X_PRICE member, Price Type ...
 * @param  mOpen: double collection, Open Prices ...
 * @param  mHigh: double collection, High Prices ...
 * @param  mLow: double collection, Low Prices ...
 * @param  mClose: double collection, Close Prices ...
 * @param  barIndex: int, Index ...
 *
 * @return ( double )
 */
double GetAppliedPrice(
    ENUM_X_PRICE mType,     // Type of Price Selection
    const double &mOpen[],  // Open Prices
    const double &mHigh[],  // High Preices
    const double &mLow[],   // Low Prices
    const double &mClose[], // Close Prices
    int barIndex            // Bar Index
)
{
    //
    double result = 0;

    //
    switch (mType)
    {
    //
    case X_PRICE_NONE:
        result = 0;
        break;

    //
    case X_PRICE_HIGH:
        result = mHigh[barIndex];
        break;

    //
    case X_PRICE_OPEN:
        result = mOpen[barIndex];
        break;

    //
    case X_PRICE_CLOSE:
        result = mClose[barIndex];
        break;

    //
    case X_PRICE_LOW:
        result = mLow[barIndex];
        break;

    //
    case X_PRICE_UP:
        result = MathMax(mOpen[barIndex], mClose[barIndex]);
        break;

    //
    case X_PRICE_DOWN:
        result = MathMin(mOpen[barIndex], mClose[barIndex]);
        break;

    //
    case X_PRICE_MEDIAN:
        result = ((mHigh[barIndex] + mLow[barIndex]) / 2.0);
        break;

    //
    case X_PRICE_BODY_MEDIAN:
        result = ((mOpen[barIndex] + mClose[barIndex]) / 2.0);
        break;

    //
    case X_PRICE_TYPICAL:
        result = ((mHigh[barIndex] + mLow[barIndex] + mClose[barIndex]) / 3.0);
        break;

    //
    case X_PRICE_WEIGHTED:
        result = (((mHigh[barIndex] + mLow[barIndex] + mClose[barIndex] + mClose[barIndex]) / 4.0));
        break;
    }

    //
    return result;
}

/**
 * Calculate Applied Price ...
 *
 * @param  mType: ENUM_X_PRICE member, Price Type ...
 * @param  mOpen: double, Open Price ...
 * @param  mHigh: double, High Price ...
 * @param  mLow: double, Low Price ...
 * @param  mClose: double, Close Price ...
 * @param  barIndex: int, Index ...
 *
 * @return ( double )
 */
double GetAppliedPrice(
    ENUM_X_PRICE mType, // Type of Price Selection
    double mOpen,       // Open Prices
    double mHigh,       // High Preices
    double mLow,        // Low Prices
    double mClose       // Close Prices
)
{
    //
    double result = 0;

    //
    switch (mType)
    {
    //
    case X_PRICE_NONE:
        result = 0;
        break;

    //
    case X_PRICE_HIGH:
        result = mHigh;
        break;

    //
    case X_PRICE_OPEN:
        result = mOpen;
        break;

    //
    case X_PRICE_CLOSE:
        result = mClose;
        break;

    //
    case X_PRICE_LOW:
        result = mLow;
        break;

    //
    case X_PRICE_UP:
        result = MathMax(mOpen, mClose);
        break;

    //
    case X_PRICE_DOWN:
        result = MathMin(mOpen, mClose);
        break;

    //
    case X_PRICE_MEDIAN:
        result = ((mHigh + mLow) / 2.0);
        break;

    //
    case X_PRICE_BODY_MEDIAN:
        result = ((mOpen + mClose) / 2.0);
        break;

    //
    case X_PRICE_TYPICAL:
        result = ((mHigh + mLow + mClose) / 3.0);
        break;

    //
    case X_PRICE_WEIGHTED:
        result = (((mHigh + mLow + mClose + mClose) / 4.0));
        break;
    }

    //
    return result;
}

/**
 * Calculate Applied Price ...
 *
 * @param  mSymbol: string, Specified Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Specified PEriod ...
 * @param  barIndex: int, Specified Bar Index ...
 * @param  mType: ENUM_X_PRICE member, Specified Price Type ...
 *
 * @return ( double )
 */
double GetAppliedPrice(
    string mSymbol = NULL,             // Symbol
    ENUM_TIMEFRAMES mPeriod = NULL,    // Period
    int barIndex = 0,                  // Bar Index
    ENUM_X_PRICE mType = X_PRICE_CLOSE // Type of Price Selection
)
{
    //
    double result = 0;

    //
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);
    barIndex = NormalizeInt(barIndex, 0);

    //
    bool has = IsValid(mType);
    if (!has)
    {
        return result;
    }

    //
    double open = iOpen(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    double high = iHigh(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    double low = iLow(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    double close = iClose(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    // Retrieve Applied Price ...
    result = GetAppliedPrice(
        mType,
        open,
        high,
        low,
        close //
    );

    //
    return result;
}

/**
 * Calculate Applied Price ...
 *
 * @param  mSymbol: string, Specified Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Specified PEriod ...
 * @param  time: int, Specified Bar Time ...
 * @param  mType: ENUM_X_PRICE member, Specified Price Type ...
 *
 * @return ( double )
 */
double GetAppliedPrice(
    string mSymbol = NULL,             // Symbol
    ENUM_TIMEFRAMES mPeriod = NULL,    // Period
    datetime mTime = NULL,             // Bar Time
    ENUM_X_PRICE mType = X_PRICE_CLOSE // Type of Price Selection
)
{
    //
    double result = 0;

    //
    mTime = NormalizeTime(mTime);
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    bool has = IsValid(mType);
    if (!has)
    {
        return result;
    }

    //
    int barIndex = GetBarIndex(
        mSymbol,
        mPeriod,
        mTime //
    );
    has = IsValidIndex(barIndex);
    if (!has)
    {
        return result;
    }

    //
    // Retrieve Applied Price ...
    result = GetAppliedPrice(
        mSymbol,
        mPeriod,
        barIndex,
        mType //
    );

    //
    return result;
}

/**
 * Calculate Price Boundary ...
 *
 * @param  upper: double collection reference, Upper Boundary ...
 * @param  lower: double collection reference, Lower Boundary ...
 * @param  mSymbol: string, Specified Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Specified PEriod ...
 * @param  mMode: ENUM_X_BOUNDARY_PRICE member, Specified Boundary Mode ...
 * @param  mLength: int, Loopback Length of Bondary ...
 * @param  barIndex: int, Specified Bar Index ...
 * @return ( int )
 */
int GetPriceBoundary(
    double &upper[],                    // Upper Boundary
    double &lower[],                    // Lower Boundary
    string mSymbol = NULL,              // Symbol
    ENUM_TIMEFRAMES mPeriod = NULL,     // Period
    ENUM_X_BOUNDARY_PRICE mMode = NULL, // Boundary Mode
    int mLength = 1,                    // Length
    int barIndex = 0                    // Bar Index
)
{
    //
    int result = 0;

    //
    Clean(upper);
    Clean(lower);

    //
    // Normalize Args ...
    mMode = NormalizeBoundary(mMode);
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);
    mLength = NormalizeInt(mLength, 1);
    barIndex = NormalizeInt(barIndex, 0);

    //
    // Prepare Boundary Price Types ...
    ENUM_X_PRICE upperType =
        mMode == X_BOUNDARY_PRICE_UP_DOWN
            ? X_PRICE_UP
        : mMode == X_BOUNDARY_PRICE_HIGH_LOW
            ? X_PRICE_HIGH
            : X_PRICE_NONE;
    ENUM_X_PRICE lowerType =
        mMode == X_BOUNDARY_PRICE_UP_DOWN
            ? X_PRICE_DOWN
        : mMode == X_BOUNDARY_PRICE_HIGH_LOW
            ? X_PRICE_LOW
            : X_PRICE_NONE;

    //
    // Check Boundary Type Validation ...
    bool isValid = IsValid(lowerType) &&
                   IsValid(upperType);
    if (!isValid)
    {
        return result;
    }

    //
    // Loop through Loopbacks ...
    int start = barIndex + mLength;
    int end = barIndex;
    for (int i = start; i >= end; i--)
    {
        //
        double iUpper = GetAppliedPrice(
            mSymbol,
            mPeriod,
            i,
            upperType //
        );
        double iLower = GetAppliedPrice(
            mSymbol,
            mPeriod,
            i,
            lowerType //
        );

        //
        Add(
            iUpper,
            upper //
        );

        //
        Add(
            iLower,
            lower //
        );
    }

    //
    result = ArraySize(upper);

    //
    return result;
}

/**
 * Get Applied Price Buffer ...
 *
 * @param  mType: ENUM_X_PRICE, Specified Price Type ...
 * @param  dest: double, Holds Destination Prices ...
 * @param  mOpen: double collection, Open Prices ...
 * @param  mHigh: double collection, High Prices ...
 * @param  mLow: double collection, Low Prices ...
 * @param  mClose: double collection, Close Prices ...
 *
 * @return ( int )
 */
int GetAppliedPrice(
    ENUM_X_PRICE mType,
    double &dest[],
    const double &mOpen[], // Open Prices
    const double &mHigh[], // High Preices
    const double &mLow[],  // Low Prices
    const double &mClose[] // Close Prices
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Check ArraySizes ...
    int count = ArraySize(mOpen);
    count = MathMin(count, ArraySize(mHigh));
    count = MathMin(count, ArraySize(mClose));
    count = MathMin(count, ArraySize(mLow));

    //
    // Validate ...
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    // Loopback ...
    for (int i = 0; i < count; i++)
    {
        //
        double iPrice = GetAppliedPrice(
            mType,
            mOpen[i],
            mHigh[i],
            mLow[i],
            mClose[i] //
        );

        //
        Add(
            iPrice,
            dest //
        );
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

//
// END Price ...
//

//
// START Bar ...
//

/**
 * Retrieve Specific Bar's Time ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Provided Period ...
 * @param  barIndex: int, Provided Bar Index ...
 *
 * @return ( datetime )
 */
datetime GetBarTime(
    string mSymbol = NULL,          // Specify Symbol
    ENUM_TIMEFRAMES mPeriod = NULL, // Specify TimeFrame
    int barIndex = 0                // Specify Bar Index
)
{
    //
    // Validate and Normalize Args ...

    //
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    // Retrieve Bar Time ...
    datetime result = iTime(
        mSymbol,
        mPeriod,
        barIndex //
    );

    //
    return result;
}

/**
 * Retrieve Specific Bar's Index ...
 *
 * @param  mSymbol: string, Provided Symbol ...
 * @param  mPeriod: ENUM_TIMEFRAMES member, Provided Period ...
 * @param  barTime: datetime, Provided Bar Time ...
 *
 * @return ( datetime )
 */
int GetBarIndex(
    string mSymbol = NULL,          // Specify Symbol
    ENUM_TIMEFRAMES mPeriod = NULL, // Specify TimeFrame
    datetime barTime = NULL         // Specify Bar Time
)
{
    //
    // Validate and Normalize Args ...

    //
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    // Retrieve Bar Time ...
    int result = iBarShift(
        mSymbol,
        mPeriod,
        barTime //
    );

    //
    return result;
}

//
// END Bar ...
//

//
// START Chart Info ...
//

ENUM_CHART_MODE GetChartMode(long chartId = 0)
{
    return (ENUM_CHART_MODE)ChartGetInteger(chartId, CHART_MODE);
}

color GetChartUpColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_CHART_UP);
}

color GetChartDownColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_CHART_DOWN);
}

color GetChartBullishColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BULL);
}

color GetChartBearishColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BEAR);
}

color GetChartLineColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_CHART_LINE);
}

color GetChartForeGroundColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_FOREGROUND);
}

color GetChartBackGroundColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_BACKGROUND);
}

color GetChartGridColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_GRID);
}

color GetChartBidLineColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_BID);
}

color GetChartAskLineColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_ASK);
}

color GetChartStopColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_STOP_LEVEL);
}

color GetChartVolumesColor(long chartId = 0)
{
    return (color)ChartGetInteger(chartId, CHART_COLOR_VOLUME);
}

bool GetChartShowBidLine(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_SHOW_BID_LINE);
}

bool GetChartShowAskLine(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_SHOW_ASK_LINE);
}

bool GetChartShowGrid(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_SHOW_GRID);
}

bool GetChartShowVolumes(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_SHOW_VOLUMES);
}

bool GetChartShowTradeLevels(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_SHOW_TRADE_LEVELS);
}

bool GetChartAutoScroll(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_AUTOSCROLL);
}

bool GetChartQuickNavigation(long chartId = 0)
{
    return (bool)ChartGetInteger(chartId, CHART_QUICK_NAVIGATION);
}

//
//
//

bool SetChartMode(
    ENUM_CHART_MODE value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_MODE, value);
}

bool SetChartShowBidLine(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_SHOW_BID_LINE, value);
}

bool SetChartShowAskLine(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_SHOW_ASK_LINE, value);
}

bool SetChartShowGrid(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_SHOW_GRID, value);
}

bool SetChartShowVolumes(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_SHOW_VOLUMES, value);
}

bool SetChartShowTradeLevels(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_SHOW_TRADE_LEVELS, value);
}

bool SetChartAutoScroll(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_AUTOSCROLL, value);
}

bool SetChartQuickNavigation(
    bool value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_QUICK_NAVIGATION, value);
}

bool SetChartForeGroundColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_FOREGROUND, value);
}

bool SetChartBackGroundColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_BACKGROUND, value);
}

bool SetChartUpColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_CHART_UP, value);
}

bool SetChartDownColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_CHART_DOWN, value);
}

bool SetChartBullishColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_CANDLE_BULL, value);
}

bool SetChartBearishColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_CANDLE_BEAR, value);
}

bool SetChartGridColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_GRID, value);
}

bool SetChartBidLineColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_BID, value);
}

bool SetChartAskLineColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_ASK, value);
}

bool SetChartStopColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_STOP_LEVEL, value);
}

bool SetChartLineColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_CHART_LINE, value);
}

bool SetChartVolumesColor(
    color value,
    long chartId = 0 //
)
{
    return ChartSetInteger(chartId, CHART_COLOR_VOLUME, value);
}

//
// END Chart Info ...
//

//
// START ATR ...
//

/**
 * Calculate True Range of Specified Bar ...
 *
 * @param  _symbol: string, Symbol ...
 * @param  _period: ENUM_TIMEFRAMES, period ...
 * @param  _barIndex: int, Specified Bar Index ...
 * @param  ignorePC: bool, Ignore Previous Bar ...
 *
 * @return ( double )
 */
double GetTrueRange(
    string _symbol = NULL,
    ENUM_TIMEFRAMES _period = NULL,
    int _barIndex = 0,
    bool ignorePC = false // Ignore Previous Close ...
)
{
    //
    double result = EMPTY_VALUE;

    //
    // Normalize ...
    _symbol = NormalizeSymbol(_symbol);
    _period = NormalizePeriod(_period);
    _barIndex = NormalizeInt(_barIndex, 0);

    //
    double _low = iLow(_symbol, _period, _barIndex);
    double _high = iHigh(_symbol, _period, _barIndex);
    double _pLow = iLow(_symbol, _period, _barIndex + 1);
    double _pHigh = iHigh(_symbol, _period, _barIndex + 1);
    double _pClose = iClose(_symbol, _period, _barIndex + 1);

    //
    double hlDiff = _high - _low;

    //
    if (!ignorePC)
    {
        //
        double hpcDiff = MathAbs(_high - _pClose);
        double lpcDiff = MathAbs(_high - _pClose);

        //
        result = MathMax(hlDiff, hpcDiff);
        result = MathMax(result, lpcDiff);
    }
    else
    {
        result = hlDiff;
    }

    //
    return result;
}

/**
 * Calculate ATR ...
 *
 * @param  _symbol: string, Symbol ...
 * @param  _period: ENUM_TIMEFRAMES, period ...
 * @param  _barIndex: int, Specified Bar Index ...
 * @param  _length: int ATR Period ...
 *
 * @return ( double )
 */
double GetAverageTrueRange(
    string _symbol = NULL,
    ENUM_TIMEFRAMES _period = NULL,
    int _barIndex = 0,
    int _length = 14 //
)
{
    //
    double result = EMPTY_VALUE;

    //
    // Normalize ...
    _symbol = NormalizeSymbol(_symbol);
    _period = NormalizePeriod(_period);
    _length = NormalizeInt(_length, 2);
    _barIndex = NormalizeInt(_barIndex, 0);

    //
    // Collect True Ranges ...
    double itr;
    double _trs[];
    bool has = false;
    int start = _barIndex;
    int end = start + _length;
    for (int i = start; i < end; i++)
    {
        //
        itr = GetTrueRange(
            _symbol,
            _period,
            _barIndex,
            i == end - 1 // Ignore PC ...
        );
        has = NotEmptyZero(itr);
        if (has)
        {
            //
            Add(
                itr,
                _trs //
            );
        }
    }

    //
    int count = ArraySize(_trs);
    has = HasChild(_trs);
    if (!has)
    {
        return result;
    }

    //
    result = GetAverage(_trs);

    //
    // Cleanup ...
    XClean(_trs);

    //
    return result;
}

//
// END َATR ...
//

//
// START Linear Regression ...
//

/**
 * Calculate Slope and Intercept for Linear Regression Predictions ...
 *
 * @param  _slope: double, refrence to Hold Calculated Slope ...
 * @param  _intercept: double, reference to Hold Calculated Intercept ...
 * @param  xData: double, reference Collection to Provides X-Axis Data for Calculations ...
 * @param  yData: double, reference Collection to Provides Y-Axis Data for Calculations ...
 *
 * @return ( int )
 */
int CalculateSlopeAndIntercept(
    double &_slope,
    double &_intercept,
    double &xData[],
    double &yData[] //
)
{
    //
    int result = 0;

    //
    bool has = false;

    //
    _slope = 0;
    _intercept = 0;

    //
    if (!HasChild(xData) ||
        !HasChild(yData))
    {
        return result;
    }

    //
    double sumX = 0;
    double sumY = 0;
    double sumXY = 0;
    double sumX2 = 0;

    //
    double x = 0;
    double y = 0;
    int end = MathMin(ArraySize(xData), ArraySize(yData));
    for (int i = 0; i < end; i++)
    {
        //
        double x = xData[i];
        double y = yData[i];

        //
        sumX += x;
        sumY += y;
        sumXY += x * y;
        sumX2 += x * x;
    }

    //
    int n = end;
    double _dividedBy = (n * sumX2 - sumX * sumX);
    // if (_dividedBy <= 0 || n <= 0)
    // {
    //     //
    //     _slope = 0;
    //     _intercept = 0;

    //     //
    //     result = 0;
    // } else {
    //     //
    //     _slope = (n * sumXY - sumX * sumY) / _dividedBy;
    //     _intercept = (sumY - _slope * sumX) / n;

    //     //
    //     result = n;
    // }

    //
    _slope = (n * sumXY - sumX * sumY) / _dividedBy;
    _intercept = (sumY - _slope * sumX) / n;

    //
    result = n;

    //
    return result;
}

/**
 * Predict Value for Specified X ...
 *
 * @param  _sllope: double, Provided Slope ...
 * @param  _intercept: double, Provided Intercept ...
 * @param  _forX: double, Specified XValue ...
 *
 * @return ( double )
 */
double PredictValue(
    double _slope,
    double _intercept,
    double _forX //
)
{
    //
    double result = 0;

    //
    // Validate ...
    if (!NotEmpty(_forX) ||
        !NotEmpty(_slope) ||
        !NotEmpty(_intercept))
    {
        return result;
    }

    //
    result = (_intercept + (_slope * _forX));

    //
    return result;
}

//
// END Linear Regression ...
//

//


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.common.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Common Library
// --------------------------------------
// Name: XCommonLib
// Description: provide all commonly used functions ...
//
//
// 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

//
// Imports ...
#include "../Libraries/x-saherelm.common.extensions.lib.mq5"
#include "../Libraries/x-saherelm.common.models.lib.mq5"
#include "../Libraries/x-saherelm.x-ohcl.lib.mq5"

//
// Models ...

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.common.models.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Common Library
// --------------------------------------
// Name: XModelsLib
// Description: provide all commonly used functions ...
//
//
// 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.extensions.lib.mq5"

//
// Use To Parse Symbols for
// Ignore Brokers Additional Suffixes ...
struct XSymbolParser
{
    //
    string version; // Temp ...

    //
    // Symbol Parsing ...

    //
    // Forext ...

    //
    bool IsEURUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsUSDJPY(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "USD",
            "JPY" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURJPY(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "JPY" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsUSDCHF(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "USD",
            "CHF" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURCHF(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "CHF" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsGBPUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "GBP",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURGBP(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "GBP" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsAUDUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "AUD",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURAUD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "AUD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsUSDCAD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "USD",
            "CAD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURCAD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "CAD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsNZDUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "NZD",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsEURNZD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "EUR",
            "NZD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    // Metals ...

    //
    bool IsXAUUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "XAU",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    bool IsXAGUSD(string symbol)
    {
        //
        string symbolIdentifiers[] = {
            "XAG",
            "USD" //
        };

        //
        bool result = Contains(
            symbolIdentifiers,
            symbol //
        );

        //
        return result;
    }

    //
    // Indexes ...

    //
    bool IsDowJones(string symbol)
    {
        //
        bool result = Contains(
            "DowJones30",
            symbol //
        );

        //
        return result;
    }

    //
    // Oil ...

    //
    bool IsBrent(string symbol)
    {
        //
        bool result = Contains(
            "BRENT",
            symbol //
        );

        //
        return result;
    }

    //
    // Symbol Finder ...
    string GetEURUSDSymbol()
    {
        //
        string result = NULL;

        //
        string symbols[];
        int symbolsCount = GetAllSymbols(
            symbols //
        );
        if (!IsValidSize(symbolsCount))
        {
            return result;
        }

        //
        for (int i = 0; i < symbolsCount; i++)
        {
            //
            string iSymbol = symbols[i];
            if (IsEURUSD(iSymbol))
            {
                //
                result = iSymbol;
                break;
            }
        }

        //
        return result;
    }
};


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.draw.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Draw Library
// --------------------------------------
// Name: XDraw
// Description: provides Draw abilities ...
//
//
// 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

//
// Global Definitions: Variables, Properties and etc ...

//
// Retrieve Chart Size in Pixel ...
struct XSize
{
    long width;
    long height;

    //
    int x;
    int y;
    int w;
    int h;

    //
    int wStep;
    int hStep;
};

//
// Define a Draw Specifications ...
struct XDrawSpecifications
{
    //
    // Props ...

    //
    bool draw; // Draw or Not ...

    //
    int width;             // Draw Witdth ...
    color clr;             // Draw Color ...
    ENUM_LINE_STYLE style; // Draw Style ...

    //
    bool back;      // in the background
    bool fill;      // fill object if supported
    bool selection; // highlight to move
    bool rayRight;  // line's continuation to the right
    bool hidden;    // hidden in the object list
    long zOrder;    // priority for mouse click

    //
    string label;                  // Label ...
    color labelColor;              // Label Color ...
    double labelAngel;             // Label Angle ...
    string labelFont;              // Label Font ...
    int labelFontSize;             // Label Font Size ...
    ENUM_ANCHOR_POINT labelAnchor; // Label Anchor Point ...

    //
    // Constructor ...
    XDrawSpecifications()
    {
        Clean();
    }

    //
    // Operator ...
    void operator=(const XDrawSpecifications &value)
    {
        //
        clr = value.clr;
        draw = value.draw;
        width = value.width;
        style = value.style;

        //
        back = value.back;
        hidden = value.hidden;
        zOrder = value.zOrder;
        rayRight = value.rayRight;
        selection = value.selection;

        //
        fill = value.fill;

        //
        label = value.label;
        labelColor = value.labelColor;
        labelAngel = value.labelAngel;
        labelFont = value.labelFont;
        labelFontSize = value.labelFontSize;
        labelAnchor = value.labelAnchor;
    }

    //
    // Functions ...

    //
    void Clean()
    {
        //
        draw = false;
        width = 1;
        clr = CLR_NONE;
        style = STYLE_SOLID;

        //
        back = false;
        hidden = false;
        rayRight = false;
        selection = false;
        fill = false;

        //
        zOrder = 0;

        //
        label = "";
        labelColor = CLR_NONE;
        labelAngel = 0;
        labelFont = "Arial";
        labelFontSize = 20;
        labelAnchor = ANCHOR_LEFT_UPPER;

        //
        ZeroMemory(this);
    }

    //
    void Label(
        string mLabel = "",                                 // Label ...
        color mLabelColor = CLR_NONE,                       // Label Color ...
        string mLabelFont = "Arial",                        // Label Font ...
        int mLabelFontSize = 20,                            // Label Font Size ...
        ENUM_ANCHOR_POINT mLabelAnchor = ANCHOR_LEFT_UPPER, // Label Anchor Point ...
        double mLabelAngel = 0                              // Label Angle ...
    )
    {
        //
        label = mLabel;
        labelFont = mLabelFont;
        labelColor = mLabelColor;
        labelAngel = mLabelAngel;
        labelAnchor = mLabelAnchor;
        labelFontSize = mLabelFontSize;
    }
};

//
// Candle Draw Specifications ...
struct XOHCLDrawSpecification
{
    //
    // Props ...
    XDrawSpecifications openStyle;
    XDrawSpecifications highStyle;
    XDrawSpecifications closeStyle;
    XDrawSpecifications lowStyle;

    //
    // Constructors ...
    XOHCLDrawSpecification()
    {
        Clean();
    }

    //
    // Operators ...
    void operator=(const XOHCLDrawSpecification &value)
    {
        //
        lowStyle = value.lowStyle;
        openStyle = value.openStyle;
        highStyle = value.highStyle;
        closeStyle = value.closeStyle;
    }

    //
    // Functions ...

    //
    void Clean()
    {
        //
        lowStyle.Clean();
        openStyle.Clean();
        highStyle.Clean();
        closeStyle.Clean();

        //
        ZeroMemory(this);
    }

    //
    void Default()
    {
        //
        // Open ...
        openStyle.width = 1;
        openStyle.draw = true;
        openStyle.clr = clrLime;
        openStyle.style = STYLE_SOLID;
        openStyle.Label(
            "O",
            openStyle.clr);

        //
        // High ...
        highStyle.width = 1;
        highStyle.draw = true;
        highStyle.clr = clrAqua;
        highStyle.style = STYLE_SOLID;
        highStyle.Label(
            "H",
            highStyle.clr);

        //
        // Close ...
        closeStyle.width = 1;
        closeStyle.draw = true;
        closeStyle.clr = clrRed;
        closeStyle.style = STYLE_SOLID;
        closeStyle.Label(
            "C",
            closeStyle.clr);

        //
        // Low ...
        lowStyle.width = 1;
        lowStyle.draw = true;
        lowStyle.clr = clrMagenta;
        lowStyle.style = STYLE_SOLID;
        lowStyle.Label(
            "L",
            lowStyle.clr);
    }

    //
    void Width(int value)
    {
        //
        lowStyle.width = value;
        openStyle.width = value;
        highStyle.width = value;
        closeStyle.width = value;
    }

    //
    void Style(ENUM_LINE_STYLE value)
    {
        //
        lowStyle.style = value;
        openStyle.style = value;
        highStyle.style = value;
        closeStyle.style = value;
    }

    //
    void Color(color value)
    {
        //
        lowStyle.clr = value;
        openStyle.clr = value;
        highStyle.clr = value;
        closeStyle.clr = value;
    }

    //
    void RayRight(bool value)
    {
        //
        lowStyle.rayRight = value;
        openStyle.rayRight = value;
        highStyle.rayRight = value;
        closeStyle.rayRight = value;
    }

    //
    void Draw(bool value)
    {
        //
        lowStyle.draw = value;
        openStyle.draw = value;
        highStyle.draw = value;
        closeStyle.draw = value;
    }

    //
    void Back(bool value)
    {
        //
        lowStyle.back = value;
        openStyle.back = value;
        highStyle.back = value;
        closeStyle.back = value;
    }

    //
    void Fill(bool value)
    {
        //
        lowStyle.fill = value;
        openStyle.fill = value;
        highStyle.fill = value;
        closeStyle.fill = value;
    }

    //
    void Label(string value)
    {
        //
        lowStyle.Label(value);
        openStyle.Label(value);
        highStyle.Label(value);
        closeStyle.Label(value);
    }

    //
    void SetLabelPrefix(string value)
    {
        //
        lowStyle.label = value + " " + lowStyle.label;
        openStyle.label = value + " " + openStyle.label;
        highStyle.label = value + " " + highStyle.label;
        closeStyle.label = value + " " + closeStyle.label;
    }
};

//
string drawPrefix = "X-OBJ";

//
// Common Draw Functions ...

//
// remove drawn objects ...
void RemoveDraws(
    string tag = NULL,
    long chartId = -1,
    int subWindow = -1 //
)
{
    //
    // Normalize Args ...
    //
    if (tag == NULL)
    {
        tag = drawPrefix;
    }

    //
    if (chartId == -1)
    {
        chartId = 0;
    }

    //
    if (subWindow == -1)
    {
        subWindow = 0;
    }

    //
    for (int iObj = ObjectsTotal(chartId, subWindow) - 1; iObj >= 0; iObj--)
    {
        //
        string on = ObjectName(chartId, iObj, subWindow);
        if (StringFind(on, tag) >= 0)
        {
            ObjectDelete(chartId, on);
        }
    }

    //
    // Redraw Chart ...
    ChartRedraw();
}

//
bool RemoveDraw(
    string name,
    long chartId = -1,
    int subWindow = -1 //
)
{
    //
    bool result = false;

    //
    if (chartId == -1)
    {
        chartId = 0;
    }

    //
    if (subWindow == -1)
    {
        subWindow = 0;
    }

    //
    string objName = "";
    for (int iObj = ObjectsTotal(chartId, subWindow) - 1; iObj >= 0; iObj--)
    {
        //
        string on = ObjectName(chartId, iObj, subWindow);
        if (StringFind(on, name) >= 0)
        {
            //
            objName = on;
            break;
        }
    }

    //
    if (StringLen(objName) > 0)
    {
        result = ObjectDelete(chartId, objName);
    }

    //
    return result;
}

//
// Deselect All Draws ...
void DeselectAllDraws()
{
    //
    int objs = ObjectsTotal(ChartID(), 0);
    for (int o = 0; o < objs; o++)
    {
        ObjectSetInteger(ChartID(), ObjectName(ChartID(), o, 0), OBJPROP_SELECTED, false);
    }

    //
    ChartRedraw();
}

//
// Check Object Exists ...
bool IsDrawExists(
    string name,
    long chartId = -1,
    int subWindow = -1 //
)
{
    //
    bool result = false;

    //
    if (chartId == -1)
    {
        chartId = 0;
    }

    //
    if (subWindow == -1)
    {
        subWindow = 0;
    }

    //
    for (int iObj = ObjectsTotal(chartId, subWindow) - 1; iObj >= 0; iObj--)
    {
        //
        string on = ObjectName(chartId, iObj, subWindow);
        result = StringFind(on, name) >= 0;
        if (result)
        {
            break;
        }
    }

    //
    return result;
}

//
// Find Specific Object Name ...
string FindObjectName(
    string suggest,
    long chartId = 0,
    int subWindow = 0 //
)
{
    //
    string result = NULL;

    //
    int objectsCount = ObjectsTotal(chartId, subWindow);
    if (objectsCount <= 0)
    {
        return result;
    }

    //
    for (int i = objectsCount - 1; i >= 0; i--)
    {
        //
        string iObjectName = ObjectName(chartId, i, subWindow);
        bool isExists = StringFind(iObjectName, suggest) >= 0;
        if (isExists)
        {
            //
            result = iObjectName;
            break;
        }
    }

    //
    return result;
}

//
// Regular Draws ...

//
// Arrow Functions ...

//
// Draw an Arrow Object on Chart ...
bool DrawArrow(
    const long chartID = 0,                         // chart's ID
    const string name = "Arrow",                    // arrow name
    const int subWindow = 0,                        // subwindow index
    datetime time = 0,                              // anchor point time
    double price = 0,                               // anchor point price
    const uchar arrowCode = 252,                    // arrow code
    const ENUM_ARROW_ANCHOR anchor = ANCHOR_BOTTOM, // anchor point position
    const color clr = clrAqua,                      // arrow color
    const ENUM_LINE_STYLE style = STYLE_SOLID,      // border line style
    const int width = 3,                            // arrow size
    const bool back = false,                        // in the background
    const bool selection = false,                   // highlight to move
    const bool hidden = false,                      // hidden in the object list
    const long zOrder = 0                           // Order Object ...
)
{
    //
    bool result = false;

    //
    // set anchor point coordinates if they are not set ...
    ChangeArrowEmptyPoint(time, price);

    //
    // reset the error value ...
    ResetLastError();

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create an arrow ...
    result = ObjectCreate(chartID, objName, OBJ_ARROW, subWindow, time, price);
    if (!result)
    {
        return result;
    }

    //
    // set the arrow code ...
    ObjectSetInteger(chartID, objName, OBJPROP_ARROWCODE, arrowCode);

    //
    // set anchor type ...
    ObjectSetInteger(chartID, objName, OBJPROP_ANCHOR, anchor);

    //
    // set the arrow color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set the border line style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set the arrow's size ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the arrow by mouse
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is false by default, if you want making it possible to highlight and move the object
    // change it to true ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move the anchor point of Arrow ...
bool MoveArrow(
    const long chartID = 0,      // chart's ID
    const string name = "Arrow", // object name
    datetime time = 0,           // anchor point time coordinate
    double price = 0             // anchor point price coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if point position is not set, move it to the current bar having Bid price ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    if (!price)
    {
        price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }

    //
    // move the anchor point ...
    result = ObjectMove(chartID, objName, 0, time, price);

    //
    return result;
}

//
// Delete an arrow ...
bool DeleteArrow(
    const long chartID = 0,     // chart's ID
    const string name = "Arrow" // arrow name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete an arrow ...
    result = !ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Change the arrow code ...
bool ArrowCodeChange(
    const long chartID = 0,      // chart's ID
    const string name = "Arrow", // object name
    const uchar code = 252       // arrow code
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change the arrow code ...
    result = ObjectSetInteger(chartID, objName, OBJPROP_ARROWCODE, code);

    //
    return result;
}

//
// Change anchor type ...
bool ArrowAnchorChange(
    const long chartID = 0,                     // chart's ID
    const string name = "Arrow",                // object name
    const ENUM_ARROW_ANCHOR anchor = ANCHOR_TOP // anchor type
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change anchor type ...
    result = !ObjectSetInteger(chartID, objName, OBJPROP_ANCHOR, anchor);

    //
    return result;
}

//
// Check anchor point values and set default values
// for empty ones ...
void ChangeArrowEmptyPoint(
    datetime &time,
    double &price //
)
{
    //
    // if the point's time is not set, it will be on the current bar ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    // if the point's price is not set, it will have Bid value ...
    if (!price)
    {
        price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }
}

//
// Horizontal Line Functions ...

//
// Create, Draw the horizontal line ...
bool DrawHorizontalLine(
    const long chartID = 0,                    // chart's ID
    const string name = "HLine",               // line name
    const int subWindow = 0,                   // subwindow index
    double price = 0,                          // line price
    const color clr = clrRed,                  // line color
    const ENUM_LINE_STYLE style = STYLE_SOLID, // line style
    const int width = 1,                       // line width
    const bool back = false,                   // in the background
    const bool selection = false,              // highlight to move
    const bool hidden = false,                 // hidden in the object list
    const long zOrder = 0                      // priority for mouse click
)
{
    //
    bool result = false;

    //
    // if the price is not set, set it at the current Bid price level ...
    if (!price)
    {
        price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create a horizontal line ...
    result = ObjectCreate(chartID, objName, OBJ_HLINE, subWindow, 0, price);
    if (!result)
    {
        return result;
    }

    //
    // set line color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set line display style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set line width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the line by mouse
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, selection);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move horizontal line ...
bool MoveHorizontalLine(
    const long chartID = 0,      // chart's ID
    const string name = "HLine", // line name
    double price = 0             // line price
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if the line price is not set, move it to the current Bid price level ...
    if (!price)
    {
        price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // move a horizontal line ...
    result = ObjectMove(chartID, objName, 0, 0, price);

    //
    return result;
}

//
// Delete a horizontal line ...
bool DeleteHorizontalLine(
    const long chartID = 0,     // chart's ID
    const string name = "HLine" // line name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete a horizontal line ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Vertical Line Functions ...

//
// Create the vertical line ...
bool DrawVerticalLine(
    const long chartID = 0,                    // chart's ID
    const string name = "VLine",               // line name
    const int subWindow = 0,                   // subwindow index
    datetime time = 0,                         // line time
    const color clr = clrRed,                  // line color
    const ENUM_LINE_STYLE style = STYLE_SOLID, // line style
    const int width = 1,                       // line width
    const bool back = false,                   // in the background
    const bool selection = false,              // highlight to move
    const bool hidden = false,                 // hidden in the object list
    const long zOrder = 0                      // priority for mouse click
)
{
    //
    bool result = false;

    //
    // if the line time is not set, draw it via the last bar ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create a vertical line ...
    result = ObjectCreate(chartID, objName, OBJ_VLINE, subWindow, time, 0);
    if (!result)
    {
        return result;
    }

    //
    // set line color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set line display style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set line width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the line by mouse
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move the vertical line ...
bool MoveVerticalLine(
    const long chartID = 0,      // chart's ID
    const string name = "VLine", // line name
    datetime time = 0            // line time
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if line time is not set, move the line to the last bar ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    // move the vertical line ...
    result = ObjectMove(chartID, objName, 0, time, 0);

    //
    return result;
}

//
// Delete the vertical line ...
bool DeleteVerticalLine(
    const long chartID = 0,     // chart's ID
    const string name = "VLine" // line name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the vertical line ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Rectangle Functions ...

//
// Create rectangle by the given coordinates  ...
bool DrawRectangle(
    const long chartID = 0,                    // chart's ID
    const string name = "Rectangle",           // rectangle name
    const int subWindow = 0,                   // subwindow index
    datetime time1 = 0,                        // down first point time
    double price1 = 0,                         // down first point price
    datetime time2 = 0,                        // up second point time
    double price2 = 0,                         // up second point price
    const color clr = clrRed,                  // rectangle color
    const ENUM_LINE_STYLE style = STYLE_SOLID, // style of rectangle lines
    const int width = 1,                       // width of rectangle lines
    const bool fill = false,                   // filling rectangle with color
    const bool back = false,                   // in the background
    const bool selection = true,               // highlight to move
    const bool hidden = true,                  // hidden in the object list
    const long zOrder = 0                      // priority for mouse click
)
{
    //
    bool result = false;

    //
    // Set anchor points' coordinates if they are not set ...
    ChangeRectangleEmptyPoints(time1, price1, time2, price2);

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // Create a rectangle by the given coordinates ...
    result = ObjectCreate(
        chartID,
        objName,
        OBJ_RECTANGLE,
        subWindow,
        time1,
        price1,
        time2,
        price2 //
    );
    if (!result)
    {
        return result;
    }

    //
    // Set rectangle color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // Set the style of rectangle lines
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // Set width of the rectangle lines ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // Enable (true) or disable (false) the mode of filling the rectangle ...
    ObjectSetInteger(chartID, objName, OBJPROP_FILL, fill);

    //
    // Display in the foreground (false) or background (true) ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // Enable (true) or disable (false) the mode of highlighting the rectangle for moving
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, selection);

    //
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // Hide (true) or display (false) graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // Set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // Successful execution
    return result;
}

//
// Move the rectangle anchor point ...s
bool RectanglePointChange(
    const long chartID = 0,          // chart's ID
    const string name = "Rectangle", // rectangle name
    const int pointIndex = 0,        // anchor point index
    datetime time = 0,               // anchor point time coordinate
    double price = 0                 // anchor point price coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if point position is not set, move it to the current bar having Bid price ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    if (!price)
    {
        price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // Move the anchor point ...
    result = ObjectMove(chartID, objName, pointIndex, time, price);

    //
    return result;
}

//
// Delete the rectangle ...
bool DeleteRectangle(
    const long chartID = 0,         // chart's ID
    const string name = "Rectangle" // rectangle name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Check the values of rectangle's anchor points and set default
// values for empty ones
void ChangeRectangleEmptyPoints(
    datetime &time1, double &price1,
    datetime &time2, double &price2 //
)
{
    //
    // if the first point's time is not set, it will be on the current bar ...
    if (!time1)
    {
        time1 = TimeCurrent();
    }

    //
    // if the first point's price is not set, it will have Bid value ...
    if (!price1)
    {
        price1 = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // if the second point's time is not set, it is located 9 bars left from the second one ...
    if (!time2)
    {
        //
        // array for receiving the open time of the last 10 bars ...
        datetime temp[10];
        CopyTime(Symbol(), Period(), time1, 10, temp);

        //
        // set the second point 9 bars left from the first one ...
        time2 = temp[0];
    }

    //
    // if the second point's price is not set, move it 300 points lower than the first one ...
    if (!price2)
    {
        price2 = price1 - 300 * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
    }
}

//
// RectangleLabel Functions ...

//
// Create Rectangle Label Object ...
bool DrawRectangleLabel(
    const long chartID = 0,                            // chart's ID
    const string name = "RectLabel",                   // label name
    const int subWindow = 0,                           // subwindow index
    const int x = 0,                                   // X coordinate
    const int y = 0,                                   // Y coordinate
    const int width = 50,                              // width
    const int height = 18,                             // height
    const color bgColor = clrWhiteSmoke,               // background color
    const ENUM_BORDER_TYPE border = BORDER_SUNKEN,     // border type
    const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, // chart corner for anchoring
    const color clr = clrRed,                          // flat border color (Flat)
    const ENUM_LINE_STYLE style = STYLE_SOLID,         // flat border style
    const int lineWidth = 1,                           // flat border width
    const bool back = false,                           // in the background
    const bool selection = false,                      // highlight to move
    const bool hidden = false,                         // hidden in the object list
    const long zOrder = 0                              // priority for mouse click
)
{
    //
    bool result = false;

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create a rectangle label
    result = ObjectCreate(chartID, objName, OBJ_RECTANGLE_LABEL, subWindow, 0, 0);
    if (!result)
    {
        return result;
    }

    //
    // set label coordinates ...
    ObjectSetInteger(chartID, objName, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(chartID, objName, OBJPROP_YDISTANCE, y);

    //
    // set label size ...
    ObjectSetInteger(chartID, objName, OBJPROP_XSIZE, width);
    ObjectSetInteger(chartID, objName, OBJPROP_YSIZE, height);

    //
    // set background color ...
    ObjectSetInteger(chartID, objName, OBJPROP_BGCOLOR, bgColor);

    //
    // set border type ...
    ObjectSetInteger(chartID, objName, OBJPROP_BORDER_TYPE, border);

    //
    // set the chart's corner, relative to which point coordinates are defined ...
    ObjectSetInteger(chartID, objName, OBJPROP_CORNER, corner);

    //
    // set flat border color (in Flat mode) ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set flat border line style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set flat border width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, lineWidth);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the label by mouse ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, selection);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move Rectangle Label ..
bool MoveRectangleLabel(
    const long chartID = 0,          // chart's ID
    const string name = "RectLabel", // label name
    const int x = 0,                 // X coordinate
    const int y = 0                  // Y coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // move the rectangle label ...
    result = ObjectSetInteger(chartID, objName, OBJPROP_XDISTANCE, x);
    if (!result)
    {
        return result;
    }

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_YDISTANCE, y);

    //
    return result;
}

//
// Delete the rectangle label ...
bool DeleteRectangleLabel(
    const long chartID = 0,         // chart's ID
    const string name = "RectLabel" // label name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the label ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Change Rectangle Label Size ...
bool ChangeSizeRectangleLabel(
    const long chartID = 0,          // chart's ID
    const string name = "RectLabel", // label name
    const int width = 50,            // label width
    const int height = 18            // label height
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change label size ...
    result = ObjectSetInteger(chartID, objName, OBJPROP_XSIZE, width);
    if (!result)
    {
        return result;
    }

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_YSIZE, height);

    //
    return result;
}

//
// Change rectangle label border type ...
bool RectLabelChangeBorderType(
    const long chartID = 0,                       // chart's ID
    const string name = "RectLabel",              // label name
    const ENUM_BORDER_TYPE border = BORDER_SUNKEN // border type
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change border type ...
    result = ObjectSetInteger(chartID, objName, OBJPROP_BORDER_TYPE, border);

    //
    return result;
}

//
// TrendLine Funcions ...

//
// Create a trend line by the given coordinates ...
bool DrawTrendLine(
    const long chartID = 0,                    // chart's ID
    const string name = "TrendLine",           // line name
    const int subWindow = 0,                   // subwindow index
    datetime time1 = 0,                        // first point time
    double price1 = 0,                         // first point price
    datetime time2 = 0,                        // second point time
    double price2 = 0,                         // second point price
    const color clr = clrYellow,               // line color
    const ENUM_LINE_STYLE style = STYLE_SOLID, // line style
    const int width = 1,                       // line width
    const bool back = false,                   // in the background
    const bool selection = false,              // highlight to move
    const bool rayRight = false,               // line's continuation to the right
    const bool hidden = false,                 // hidden in the object list
    const long zOrder = 0                      // priority for mouse click
)
{
    //
    bool result = false;

    //
    // set anchor points' coordinates if they are not set ...
    ChangeTrendLineEmptyPoints(
        time1,
        price1,
        time2,
        price2);

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create a trend line by the given coordinates ...
    result = ObjectCreate(chartID, objName, OBJ_TREND, subWindow, time1, price1, time2, price2);
    if (!result)
    {
        return result;
    }

    //
    // set line color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set line display style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set line width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the line by mouse
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // enable true or disable false the mode of continuation of the line's display to the right ...
    ObjectSetInteger(chartID, objName, OBJPROP_RAY_RIGHT, rayRight);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move trend line anchor point ...                                     |
bool ChangeTrendLinePoint(
    const long chartID = 0,          // chart's ID
    const string name = "TrendLine", // line name
    const int pointindex = 0,        // anchor point index
    datetime time = 0,               // anchor point time coordinate
    double price = 0                 // anchor point price coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if point position is not set, move it to the current bar having Bid price ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    if (!price)
    {
        price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // move trend line's anchor point ...
    result = ObjectMove(chartID, objName, pointindex, time, price);

    //
    return result;
}

//
// The function deletes the trend line from the chart ...
bool DeleteTrendLine(
    const long chartID = 0,         // chart's ID
    const string name = "TrendLine" // line name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete a trend line ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Check the values of trend line's anchor points and set default ...
// values for empty ones ...
void ChangeTrendLineEmptyPoints(
    datetime &time1,
    double &price1,
    datetime &time2,
    double &price2 //
)
{
    //
    // if the first point's time is not set, it will be on the current bar ...
    if (!time1)
    {
        time1 = TimeCurrent();
    }

    //
    // if the first point's price is not set, it will have Bid value ...
    if (!price1)
    {
        price1 = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    }

    //
    // if the second point's time is not set, it is located 9 bars left from the second one ...
    if (!time2)
    {
        //
        // array for receiving the open time of the last 10 bars ...
        datetime temp[10];
        CopyTime(Symbol(), Period(), time1, 10, temp);

        //
        // set the second point 9 bars left from the first one ...
        time2 = temp[0];
    }

    //
    // if the second point's price is not set, it is equal to the first point's one ...
    if (!price2)
    {
        price2 = price1;
    }
}

//
// Text Functions ...

//
// Create and Draw a Text Object ...
bool DrawText(
    const long chartID = 0,                         // chart's ID
    const string name = "Text",                     // object name
    const int subWindow = 0,                        // subwindow index
    datetime time = 0,                              // anchor point time
    double price = 0,                               // anchor point price
    const string text = "Text",                     // the text itself
    const string font = "Arial",                    // font
    const int fontSize = 10,                        // font size
    const color clr = clrRed,                       // color
    const double angle = 0.0,                       // text slope
    const ENUM_ANCHOR_POINT anchor = ANCHOR_CENTER, // anchor type
    const bool back = false,                        // in the background
    const bool selection = false,                   // highlight to move
    const bool hidden = true,                       // hidden in the object list
    const long zOrder = 0                           // priority for mouse click
)
{
    //
    bool result = false;

    //
    // set anchor point coordinates if they are not set ...
    ChangeTextEmptyPoint(time, price);

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    //
    // create Text object
    result = ObjectCreate(chartID, objName, OBJ_TEXT, subWindow, time, price);
    if (!result)
    {
        return result;
    }

    //
    // set the text ...
    ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    // set text font ...
    ObjectSetString(chartID, objName, OBJPROP_FONT, font);

    //
    // set font size ...
    ObjectSetInteger(chartID, objName, OBJPROP_FONTSIZE, fontSize);

    //
    // set the slope angle of the text ...
    ObjectSetDouble(chartID, objName, OBJPROP_ANGLE, angle);

    //
    // set anchor type ...
    ObjectSetInteger(chartID, objName, OBJPROP_ANCHOR, anchor);

    //
    // set color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving the object by mouse ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Move the anchor point ...
bool MoveText(
    const long chartID = 0,     // chart's ID
    const string name = "Text", // object name
    datetime time = 0,          // anchor point time coordinate
    double price = 0            // anchor point price coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    //
    // if point position is not set, move it to the current bar having Bid price
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    if (!price)
    {
        price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }

    //
    // move the anchor point ...
    result = ObjectMove(chartID, objName, 0, time, price);

    //
    return result;
}

//
// Change the object text ...
bool ChangeText(
    const long chartID = 0,     // chart's ID
    const string name = "Text", // object name
    const string text = "Text"  // text
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change object text ...
    result = ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    return result;
}

//
// Delete Text object ...
bool DeleteText(
    const long chartID = 0,    // chart's ID
    const string name = "Text" // object name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the object ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Check anchor point values and set default values
// for empty ones ...
void ChangeTextEmptyPoint(
    datetime &time,
    double &price //
)
{
    //
    // if the point's time is not set, it will be on the current bar ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    // if the point's price is not set, it will have Bid value ...
    if (!price)
    {
        price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }
}

//
// Fibonacchi Functions ...

//
// Create Fibonacci Retracement by the given coordinates ...
bool DrawFibonacci(
    const long chartID = 0,                    // chart's ID
    const string name = "Fibonacci",           // object name
    const int subWindow = 0,                   // subwindow index
    datetime time1 = 0,                        // first point time
    double price1 = 0,                         // first point price
    datetime time2 = 0,                        // second point time
    double price2 = 0,                         // second point price
    const color clr = clrRed,                  // object color
    const ENUM_LINE_STYLE style = STYLE_SOLID, // object line style
    const int width = 1,                       // object line width
    const bool back = false,                   // in the background
    const bool selection = true,               // highlight to move
    const bool rayRight = false,               // object's continuation to the right
    const bool hidden = true,                  // hidden in the object list
    const long zOrder = 0                      // priority for mouse click
)
{
    //
    bool result = false;

    //
    // set anchor points' coordinates if they are not set ...
    ChangeFibonacciEmptyPoints(time1, price1, time2, price2);

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // Create Fibonacci Retracement by the given coordinates ...
    result = ObjectCreate(chartID, objName, OBJ_FIBO, subWindow, time1, price1, time2, price2);
    if (!result)
    {
        return result;
    }

    //
    // set color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set line style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set line width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of highlighting the channel for moving
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // enable true or disable false the mode of continuation of the object's display to the right ...
    ObjectSetInteger(chartID, objName, OBJPROP_RAY_RIGHT, rayRight);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution ...
    return result;
}

//
// Create Fibonacci Retracement by the given coordinates ...
bool DrawCustomFibonacci(
    //
    int levels,                // number of level lines
    double &values[],          // values of level lines
    color &colors[],           // color of level lines
    ENUM_LINE_STYLE &styles[], // style of level lines
    int &widths[],             // width of level lines
    string &descriptions[],    // level Descriptions
    //
    const long chartID = 0,                  // chart's ID
    const string name = "Fibonacci",         // object name
    const int subWindow = 0,                 // subwindow index
    datetime time1 = 0,                      // first point time
    double price1 = 0,                       // first point price
    datetime time2 = 0,                      // second point time
    double price2 = 0,                       // second point price
    const color clr = clrAqua,               // object color
    const ENUM_LINE_STYLE style = STYLE_DOT, // object line style
    const int width = 1,                     // object line width
    const bool back = false,                 // in the background
    const bool selection = true,             // highlight to move
    const bool rayRight = false,             // object's continuation to the right
    const bool hidden = true,                // hidden in the object list
    const long zOrder = 0                    // priority for mouse click
)
{
    //
    bool result = false;

    //
    // set anchor points' coordinates if they are not set ...
    ChangeFibonacciEmptyPoints(time1, price1, time2, price2);

    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // Create Fibonacci Retracement by the given coordinates ...
    result = ObjectCreate(chartID, name, OBJ_FIBO, subWindow, time1, price1, time2, price2);
    if (!result)
    {
        return result;
    }

    //
    // set color ...
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set line style ...
    ObjectSetInteger(chartID, objName, OBJPROP_STYLE, style);

    //
    // set line width ...
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true ...
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of highlighting the channel for moving
    // when creating a graphical object using ObjectCreate function, the object cannot be
    // highlighted and moved by default. Inside this method, selection parameter
    // is true by default making it possible to highlight and move the object ...
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, true);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // enable true or disable false the mode of continuation of the object's display to the right ...
    ObjectSetInteger(chartID, objName, OBJPROP_RAY_RIGHT, rayRight);

    //
    // hide true or display false graphical object name in the object list ...
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart ...
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // Change Levels Of Fibonacci ...
    result = FibonacciLevelsSet(
        levels,
        values,
        colors,
        styles,
        widths,
        descriptions,
        chartID,
        objName);

    //
    // successful execution ...
    return result;
}

//
// Set number of levels and their parameters ...
bool FibonacciLevelsSet(
    int levels,                     // number of level lines
    double &values[],               // values of level lines
    color &colors[],                // color of level lines
    ENUM_LINE_STYLE &styles[],      // style of level lines
    int &widths[],                  // width of level lines
    string &descriptions[],         // level Descriptions
    const long chartID = 0,         // chart's ID
    const string name = "Fibonacci" // object name
)
{
    //
    bool result = false;

    //
    // check array sizes ...
    result = levels == ArraySize(colors) &&
             levels == ArraySize(styles) &&
             levels == ArraySize(widths) &&
             levels == ArraySize(widths);
    if (!result)
    {
        return result;
    }
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // set the number of levels ...
    ObjectSetInteger(chartID, objName, OBJPROP_LEVELS, levels);

    //
    // set the properties of levels in the loop ...
    for (int i = 0; i < levels; i++)
    {
        //
        // level value ...
        ObjectSetDouble(chartID, objName, OBJPROP_LEVELVALUE, i, values[i]);

        //
        // level color ...
        ObjectSetInteger(chartID, objName, OBJPROP_LEVELCOLOR, i, colors[i]);

        //
        // level style ...
        ObjectSetInteger(chartID, objName, OBJPROP_LEVELSTYLE, i, styles[i]);

        //
        // level width ...
        ObjectSetInteger(chartID, objName, OBJPROP_LEVELWIDTH, i, widths[i]);

        //
        // level description ...
        // ObjectSetString(chartID, name, OBJPROP_LEVELTEXT, i, DoubleToString(100 * values[i], 1));
        ObjectSetString(chartID, objName, OBJPROP_LEVELTEXT, i, descriptions[i]);
    }

    //
    // successful execution ...
    return result;
}

//
// Move Fibonacci Retracement anchor point ...
bool FibonacciPointChange(
    const long chartID = 0,          // chart's ID
    const string name = "Fibonacci", // object name
    const int pointIndex = 0,        // anchor point index
    datetime time = 0,               // anchor point time coordinate
    double price = 0                 // anchor point price coordinate
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if point position is not set, move it to the current bar having Bid price ...
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    if (!price)
    {
        price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }

    //
    // move the anchor point ...
    result = ObjectMove(chartID, objName, pointIndex, time, price);

    //
    return result;
}

//
// Delete Fibonacci Retracement ...
bool DeleteFibonachi(
    const long chartID = 0,         // chart's ID
    const string name = "Fibonacci" // object name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the object ...
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Check the values of Fibonacci Retracement anchor points and set
// default values for empty ones ...
void ChangeFibonacciEmptyPoints(
    datetime &time1,
    double &price1,
    datetime &time2,
    double &price2 //
)
{
    //
    // if the second point's time is not set, it will be on the current bar ...
    if (!time2)
    {
        time2 = TimeCurrent();
    }

    //
    // if the second point's price is not set, it will have Bid value ...
    if (!price2)
    {
        price2 = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }

    //
    // if the first point's time is not set, it is located 9 bars left from the second one ...
    if (!time1)
    {
        //
        // array for receiving the open time of the last 10 bars ...
        datetime temp[10];
        CopyTime(Symbol(), Period(), time2, 10, temp);

        //
        // set the first point 9 bars left from the second one ...
        time1 = temp[0];
    }

    //
    // if the first point's price is not set, move it 200 points below the second one ...
    if (!price1)
    {
        price1 = price2 - 200 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    }
}

//
// Event Functions ...

//
// Create an Event ...
bool EventCreate(
    const long chartID = 0,        // chart's ID
    const string name = "Event",   // object name
    const int subWindow = 0,       // subwindow index
    const string text = "Content", // event text
    datetime time = 0,             // anchor point time
    const color clr = clrRed,      // color
    const int width = 1,           // point width when highlighted
    const bool back = false,       // in the background
    const bool selection = false,  // highlight to move
    const bool hidden = true,      // hidden in the object list
    const long zOrder = 0          // priority for mouse click
)
{
    //
    bool result = false;

    //
    if (!time)
    {
        time = TimeCurrent();
    }
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    result = ObjectCreate(chartID, objName, OBJ_EVENT, subWindow, time, 0);
    if (!result)
    {
        return result;
    }

    //
    // set event text
    ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    // set color
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set anchor point width if the object is highlighted
    ObjectSetInteger(chartID, objName, OBJPROP_WIDTH, width);

    //
    // display in the foreground false or background true
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // enable true or disable false the mode of moving event by mouse
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, selection);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    // successful execution
    return result;
}

//
// Change Event object text
bool EventTextChange(
    const long chartID = 0,       // chart's ID
    const string name = "Event",  // event name
    const string text = "Content" // text
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change object text
    result = ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    return result;
}

//
// Move Event object
bool EventMove(
    const long chartID = 0,      // chart's ID
    const string name = "Event", // event name
    datetime time = 0            // time
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // if time is not set, move event to the last bar
    if (!time)
    {
        time = TimeCurrent();
    }

    //
    // move the object
    result = ObjectMove(chartID, objName, 0, time, 0);

    //
    return result;
}

//
// Delete Event object
bool EventDelete(
    const long chartID = 0,     // chart's ID
    const string name = "Event" // event name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the object
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Button Functions ...

//
// Create the button ...
bool ButtonCreate(
    const long chartID = 0,                            // chart's ID
    const string name = "Button",                      // button name
    const int subWindow = 0,                           // subwindow index
    const int x = 0,                                   // X coordinate
    const int y = 0,                                   // Y coordinate
    const int width = 50,                              // button width
    const int height = 15,                             // button height
    const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, // chart corner for anchoring
    const string text = "Button",                      // text
    const string font = "Arial",                       // font
    const int fontSize = 8,                            // font size
    const color clr = clrBlack,                        // text color
    const color bgColor = clrLightGray,                // background color
    const color borderColor = clrNONE,                 // border color
    const bool state = false,                          // pressed/released
    const bool back = false,                           // in the background
    const bool selection = false,                      // highlight to move
    const bool hidden = true,                          // hidden in the object list
    const long zOrder = 0                              // priority for mouse click
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // create the button
    result = ObjectCreate(chartID, objName, OBJ_BUTTON, subWindow, 0, 0);
    if (!result)
    {
        return result;
    }

    //
    // set button coordinates
    ObjectSetInteger(chartID, objName, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(chartID, objName, OBJPROP_YDISTANCE, y);

    //
    // set button size
    ObjectSetInteger(chartID, objName, OBJPROP_XSIZE, width);
    ObjectSetInteger(chartID, objName, OBJPROP_YSIZE, height);

    //
    // set the chart's corner, relative to which point coordinates are defined
    ObjectSetInteger(chartID, objName, OBJPROP_CORNER, corner);

    //
    // set the text
    ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    // set text font
    ObjectSetString(chartID, objName, OBJPROP_FONT, font);

    //
    // set font size
    ObjectSetInteger(chartID, objName, OBJPROP_FONTSIZE, fontSize);

    //
    // set text color
    ObjectSetInteger(chartID, objName, OBJPROP_COLOR, clr);

    //
    // set background color
    ObjectSetInteger(chartID, objName, OBJPROP_BGCOLOR, bgColor);

    //
    // set border color
    ObjectSetInteger(chartID, objName, OBJPROP_BORDER_COLOR, borderColor);

    //
    // display in the foreground false or background true
    ObjectSetInteger(chartID, objName, OBJPROP_BACK, back);

    //
    // set button state
    ObjectSetInteger(chartID, objName, OBJPROP_STATE, state);

    //
    // enable true or disable false the mode of moving the button by mouse
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, selection);
    ObjectSetInteger(chartID, objName, OBJPROP_SELECTED, selection);

    //
    // hide true or display false graphical object name in the object list
    ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN, hidden);

    //
    // set the priority for receiving the event of a mouse click in the chart
    ObjectSetInteger(chartID, objName, OBJPROP_ZORDER, zOrder);

    //
    return result;
}

//
// Move the button ...
bool ButtonMove(
    const long chartID = 0,       // chart's ID
    const string name = "Button", // button name
    const int x = 0,              // X coordinate
    const int y = 0               // Y coordinate
)
{
    //
    bool result = false;

    //
    // move the button
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_XDISTANCE, x);
    if (!result)
    {
        return result;
    }

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_YDISTANCE, y);

    //
    return result;
}

//
// Change button size ...
bool ButtonChangeSize(
    const long chartID = 0,       // chart's ID
    const string name = "Button", // button name
    const int width = 50,         // button width
    const int height = 18         // button height
)
{
    //
    bool result = false;

    //
    // change the button size
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_XSIZE, width);
    if (!result)
    {
        return result;
    }

    //
    result = ObjectSetInteger(chartID, objName, OBJPROP_YSIZE, height);

    //
    return result;
}

//
// Change corner of the chart for binding the button ...
bool ButtonChangeCorner(
    const long chartID = 0,                           // chart's ID
    const string name = "Button",                     // button name
    const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER // chart corner for anchoring
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change anchor corner
    result = ObjectSetInteger(chartID, objName, OBJPROP_CORNER, corner);

    //
    return result;
}

//
// Change button text ...
bool ButtonTextChange(
    const long chartID = 0,       // chart's ID
    const string name = "Button", // button name
    const string text = "Text"    // text
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // change object text
    result = ObjectSetString(chartID, objName, OBJPROP_TEXT, text);

    //
    return result;
}

//
// Delete the button ...
bool ButtonDelete(
    const long chartID = 0,      // chart's ID
    const string name = "Button" // button name
)
{
    //
    bool result = false;
    
    //
    // Normalize Name ...
    string objName = drawPrefix + "_" + name;

    //
    // delete the button
    result = ObjectDelete(chartID, objName);

    //
    return result;
}

//
// Chart Style Drawers ...

//
// Apply Styles On Chart ...
void ApplyChartStyle(
    long mChartId = -1,                    // chart's ID
    ENUM_CHART_MODE mMode = CHART_CANDLES, // chart's mMode
    bool mShowBidLine = true,              // show bid line
    bool mShowAskLine = true,              // show ask line
    bool mShowGrid = false,                // show grids on chart
    bool mShowVolumes = false,             // show volumes
    bool mShowTradeLevels = true,          // Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders)
    bool mAutoScroll = true,               // chart autoscroll
    bool mQuickNavigation = true,          // chart quick navigation state
    color mForeGroundColor = clrWhite,     // chart's foreground color
    color mBackGroundColor = clrBlack,     // chart's background color
    color mUpColor = clrGreen,             // Up Color
    color mDownColor = clrRed,             // Down Color
    color mBullishColor = clrGreen,        // Bullish color
    color mBearishColor = clrRed,          // Bearish color
    color mGridColor = clrGray,            // grid color
    color mBidLineColor = clrGray,         // bid line color
    color mAskLineColor = clrRed,          // ask line color
    color mLineColor = clrLime,            // line mMode and doji candlestick color
    color mStopColor = clrGold,            // Color of stop order levels (Stop Loss and Take Profit)
    color mVolumesColor = clrGreen         // volumes color
)
{
    //
    // Validate Args ...
    if (mChartId == -1)
    {
        mChartId = 0;
    }

    //
    ChartSetInteger(mChartId, CHART_MODE, mMode);
    ChartSetInteger(mChartId, CHART_SHOW_BID_LINE, mShowBidLine);
    ChartSetInteger(mChartId, CHART_SHOW_ASK_LINE, mShowAskLine);
    ChartSetInteger(mChartId, CHART_SHOW_GRID, mShowGrid);
    ChartSetInteger(mChartId, CHART_SHOW_VOLUMES, mShowVolumes);
    ChartSetInteger(mChartId, CHART_SHOW_TRADE_LEVELS, mShowTradeLevels);
    ChartSetInteger(mChartId, CHART_AUTOSCROLL, mAutoScroll);
    ChartSetInteger(mChartId, CHART_QUICK_NAVIGATION, mQuickNavigation);
    ChartSetInteger(mChartId, CHART_COLOR_FOREGROUND, mForeGroundColor);
    ChartSetInteger(mChartId, CHART_COLOR_BACKGROUND, mBackGroundColor);
    ChartSetInteger(mChartId, CHART_COLOR_CHART_UP, mUpColor);
    ChartSetInteger(mChartId, CHART_COLOR_CHART_DOWN, mDownColor);
    ChartSetInteger(mChartId, CHART_COLOR_CANDLE_BULL, mBullishColor);
    ChartSetInteger(mChartId, CHART_COLOR_CANDLE_BEAR, mBearishColor);
    ChartSetInteger(mChartId, CHART_COLOR_GRID, mGridColor);
    ChartSetInteger(mChartId, CHART_COLOR_BID, mBidLineColor);
    ChartSetInteger(mChartId, CHART_COLOR_ASK, mAskLineColor);
    ChartSetInteger(mChartId, CHART_COLOR_STOP_LEVEL, mStopColor);
    ChartSetInteger(mChartId, CHART_COLOR_CHART_LINE, mLineColor);
    ChartSetInteger(mChartId, CHART_COLOR_VOLUME, mVolumesColor);
}

//
// Tools ...

//
// Retrieve Chart Size ...
bool GetChartSize(
    XSize &result,   // Holds Result ...
    long chartID = 0 // Specified Chart ID ...
)
{
    //
    bool mResult = false;

    //
    mResult = ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0, result.width);
    if (!mResult)
    {
        return mResult;
    }

    //
    mResult = ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0, result.height);

    //
    return mResult;
}


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-enums.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Common Library
// --------------------------------------
// Name: XEnumsLib
// Description: provide all commonly used functions ...
//
//
// 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

//
// START Price Type ...
//

//
// Availables Price Types ...
enum ENUM_X_PRICE
{
    X_PRICE_NONE,        // None
    X_PRICE_HIGH,        // High
    X_PRICE_OPEN,        // Open
    X_PRICE_CLOSE,       // Close
    X_PRICE_LOW,         // Low
    X_PRICE_UP,          // Body Up
    X_PRICE_DOWN,        // Body Down
    X_PRICE_MEDIAN,      // Median
    X_PRICE_BODY_MEDIAN, // Body Median
    X_PRICE_TYPICAL,     // Typical
    X_PRICE_WEIGHTED     // Weighted
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_PRICE member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_PRICE value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_PRICE_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_PRICE value)
{
    return IsValid(value);
}

/**
 * Converts Series Mode to ENUM_X_PRICE ...
 *
 * @param  value: ENUM_SERIESMODE member ...
 *
 * @return ( ENUM_X_PRICE )
 */
ENUM_X_PRICE ToXPrice(ENUM_SERIESMODE value)
{
    //
    ENUM_X_PRICE result = X_PRICE_CLOSE;

    //
    switch (value)
    {
    //
    // High ...
    case MODE_HIGH:
        result = X_PRICE_HIGH;
        break;

    //
    // Open ...
    case MODE_OPEN:
        result = X_PRICE_OPEN;
        break;

    //
    // Low ...
    case MODE_LOW:
        result = X_PRICE_LOW;
        break;

    //
    // Close ...
    // Default ...
    case MODE_CLOSE:
    default:
        result = X_PRICE_CLOSE;
        break;
    }

    //
    return result;
}

/**
 * Converts Applied Price to ENUM_X_PRICE ...
 *
 * @param  value: ENUM_APPLIED_PRICE member ...
 *
 * @return ( ENUM_X_PRICE )
 */
ENUM_X_PRICE ToXPrice(ENUM_APPLIED_PRICE value)
{
    //
    ENUM_X_PRICE result = X_PRICE_NONE;

    //
    switch (value)
    {
    //
    case PRICE_HIGH:
        result = X_PRICE_HIGH;
        break;

    //
    case PRICE_OPEN:
        result = X_PRICE_OPEN;
        break;

    //
    case PRICE_CLOSE:
        result = X_PRICE_CLOSE;
        break;

    //
    case PRICE_LOW:
        result = X_PRICE_LOW;
        break;

    //
    case PRICE_MEDIAN:
        result = X_PRICE_MEDIAN;
        break;

    //
    case PRICE_TYPICAL:
        result = X_PRICE_TYPICAL;
        break;

    //
    case PRICE_WEIGHTED:
        result = X_PRICE_WEIGHTED;
        break;
    }

    //
    return result;
}

/**
 * Converts Price Type to Applied Price ...
 *
 * @param  value: ENUM_X_PRICE member ...
 *
 * @return ( ENUM_APPLIED_PRICE )
 */
ENUM_APPLIED_PRICE ToAppliedPrice(ENUM_X_PRICE value)
{
    //
    // Default ...
    ENUM_APPLIED_PRICE result = PRICE_CLOSE;

    //
    if (!IsValid(value) ||
        value == X_PRICE_UP ||
        value == X_PRICE_DOWN ||
        value == X_PRICE_BODY_MEDIAN)
    {
        return result;
    }

    //
    switch (value)
    {
    //
    case X_PRICE_HIGH:
        result = PRICE_HIGH;
        break;

    //
    case X_PRICE_OPEN:
        result = PRICE_OPEN;
        break;

    //
    case X_PRICE_CLOSE:
        result = PRICE_CLOSE;
        break;

    //
    case X_PRICE_LOW:
        result = PRICE_LOW;
        break;

    //
    case X_PRICE_MEDIAN:
        result = PRICE_MEDIAN;
        break;

    //
    case X_PRICE_TYPICAL:
        result = PRICE_TYPICAL;
        break;

    //
    case X_PRICE_WEIGHTED:
        result = PRICE_WEIGHTED;
        break;
    }

    //
    return result;
}
ENUM_APPLIED_PRICE ToXAppliedPrice(ENUM_X_PRICE value)
{
    return ToAppliedPrice(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_PRICE member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_PRICE value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_PRICE_NONE:
        result = "None";
        break;

    //
    case X_PRICE_HIGH:
        result = "High";
        break;

    //
    case X_PRICE_OPEN:
        result = "Open";
        break;

    //
    case X_PRICE_CLOSE:
        result = "Close";
        break;

    //
    case X_PRICE_LOW:
        result = "Low";
        break;

    //
    case X_PRICE_UP:
        result = "Body Up";
        break;

    //
    case X_PRICE_DOWN:
        result = "Body Down";
        break;

    //
    case X_PRICE_MEDIAN:
        result = "Median";
        break;

    //
    case X_PRICE_BODY_MEDIAN:
        result = "Body Median";
        break;

    //
    case X_PRICE_TYPICAL:
        result = "Typical";
        break;

    //
    case X_PRICE_WEIGHTED:
        result = "Weighted";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_PRICE value)
{
    return ToString(value);
}

//
// END Price Type ...
//

//
// START Boundary Price Type ...
//

//
// Available Boundary Price Types ...
enum ENUM_X_BOUNDARY_PRICE
{
    X_BOUNDARY_PRICE_NONE,     // None
    X_BOUNDARY_PRICE_UP_DOWN,  // Up/Down
    X_BOUNDARY_PRICE_HIGH_LOW, // High/Low
};

/**
 * Validate ...
 *
 * @param  value: ENUM_X_BOUNDARY_PRICE member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_BOUNDARY_PRICE value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_BOUNDARY_PRICE_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_BOUNDARY_PRICE value)
{
    return IsValid(value);
}
bool IsSpecifiedValid(ENUM_X_BOUNDARY_PRICE value)
{
    return IsValid(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_BOUNDARY_PRICE member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_BOUNDARY_PRICE value)
{
    //
    string result = NULL;

    //
    if (!IsValid(value))
    {
        value = X_BOUNDARY_PRICE_NONE;
    }

    //
    result = EnumToString(value);

    //
    return result;
}
string ToXString(ENUM_X_BOUNDARY_PRICE value)
{
    return ToString(value);
}

//
// END Boundary Price Type ...
//

//
// START Direction ...
//

//
// Directions ...
enum ENUM_X_DIRECTION
{
    X_DIRECTION_ALL,     // All
    X_DIRECTION_NONE,    // None
    X_DIRECTION_BULLISH, // Bullish
    X_DIRECTION_BEARISH, // Bearish
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_DIRECTION_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_DIRECTION value)
{
    return IsValid(value);
}
bool IsSpecifiedValid(ENUM_X_DIRECTION value)
{
    return IsValid(value);
}

/**
 * Validate value Has Specified Direction ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( bool )
 */
bool HasDirection(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = IsValid(value) &&
             value != X_DIRECTION_ALL;

    //
    return result;
}

/**
 * Check a Direction is Bullish or not ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( bool )
 */
bool IsBullish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BULLISH;

    //
    return result;
}
bool IsXBullish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BULLISH;

    //
    return result;
}
bool IsSpecifiedBullish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BULLISH;

    //
    return result;
}

/**
 * Check a Direction is Bearish or not ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( bool )
 */
bool IsBearish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BEARISH;

    //
    return result;
}
bool IsXBearish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BEARISH;

    //
    return result;
}
bool IsSpecifiedBearish(ENUM_X_DIRECTION value)
{
    //
    bool result = false;

    //
    result = value == X_DIRECTION_BEARISH;

    //
    return result;
}

/**
 * Converts Specified Direction to Opposit ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( ENUM_X_DIRECTION )
 */
ENUM_X_DIRECTION Opposit(ENUM_X_DIRECTION value)
{
    //
    ENUM_X_DIRECTION result = X_DIRECTION_NONE;

    //
    if (!HasDirection(value))
    {
        return result;
    }

    //
    result =
        IsBullish(value)
            ? X_DIRECTION_BEARISH
            : X_DIRECTION_BULLISH;

    //
    return result;
}
ENUM_X_DIRECTION XOpposit(ENUM_X_DIRECTION value)
{
    return Opposit(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_DIRECTION value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    case X_DIRECTION_ALL:
        result = "ALL";
        break;
    case X_DIRECTION_NONE:
        result = "NONE";
        break;
    case X_DIRECTION_BULLISH:
        result = "BULLISH";
        break;
    case X_DIRECTION_BEARISH:
        result = "BEARISH";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_DIRECTION value)
{
    return ToString(value);
}
//
// END Direction ...
//

//
// START Fibo Level ...
//

//
// Fibonacci Levels ...
enum ENUM_X_FIBO_LEVELS
{
    X_FIBO_LEVEL_236, // 0.236
    X_FIBO_LEVEL_270, // 0.27
    X_FIBO_LEVEL_382, // 0.382
    X_FIBO_LEVEL_500, // 0.5
    X_FIBO_LEVEL_618, // 0.618
    X_FIBO_LEVEL_730, // 0.730
    X_FIBO_LEVEL_764, // 0.764
    X_FIBO_LEVEL_786, // 0.786
};

//
// Extensions ...

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_FIBO_LEVELS member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_FIBO_LEVELS value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_FIBO_LEVEL_236:
        result = "0.236";
        break;

    //
    case X_FIBO_LEVEL_270:
        result = "0.270";
        break;

    //
    case X_FIBO_LEVEL_382:
        result = "0.382";
        break;

    //
    case X_FIBO_LEVEL_500:
        result = "0.500";
        break;

    //
    case X_FIBO_LEVEL_618:
        result = "0.618";
        break;

    //
    case X_FIBO_LEVEL_730:
        result = "0.730";
        break;

    //
    case X_FIBO_LEVEL_764:
        result = "0.764";
        break;

    //
    case X_FIBO_LEVEL_786:
        result = "0.786";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_FIBO_LEVELS value)
{
    return ToString(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_FIBO_LEVELS member ...
 *
 * @return ( double )
 */
double GetFiboLevelValue(ENUM_X_FIBO_LEVELS value)
{
    //
    double result = 0;

    //
    switch (value)
    {
    //
    case X_FIBO_LEVEL_236:
        result = 0.236;
        break;

    //
    case X_FIBO_LEVEL_270:
        result = 0.270;
        break;

    //
    case X_FIBO_LEVEL_382:
        result = 0.382;
        break;

    //
    case X_FIBO_LEVEL_500:
        result = 0.500;
        break;

    //
    case X_FIBO_LEVEL_618:
        result = 0.618;
        break;

    //
    case X_FIBO_LEVEL_730:
        result = 0.730;
        break;

    //
    case X_FIBO_LEVEL_764:
        result = 0.764;
        break;

    //
    case X_FIBO_LEVEL_786:
        result = 0.786;
        break;
    }

    //
    return result;
}

//
// END Fibo Level ...
//

//
// START Swing Types ...
//

//
// Swing Types ...
enum ENUM_X_SWING_TYPE
{
    X_SWING_NONE, // None
    X_SWING_LOW,  // Swing Low
    X_SWING_HIGH, // Swing High
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_DIRECTION member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_SWING_TYPE value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_SWING_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_SWING_TYPE value)
{
    return IsValid(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_SWING_TYPE member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_SWING_TYPE value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_SWING_NONE:
        result = "None";
        break;

    //
    case X_SWING_LOW:
        result = "Swing Low";
        break;

    //
    case X_SWING_HIGH:
        result = "Swing High";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_SWING_TYPE value)
{
    return ToString(value);
}

/**
 * Converts To ENUM_SERIESMODE ...
 *
 * @param  value: ENUM_X_SWING_TYPE member ...
 *
 * @return ( ENUM_SERIESMODE )
 */
ENUM_SERIESMODE ToSeriesMode(ENUM_X_SWING_TYPE value)
{
    //
    ENUM_SERIESMODE result = MODE_HIGH;

    //
    switch (value)
    {
    //
    case X_SWING_HIGH:
        result = MODE_HIGH;
        break;

    //
    case X_SWING_LOW:
    default:
        result = MODE_LOW;
        break;
    }

    //
    return result;
}
ENUM_SERIESMODE ToXSeriesMode(ENUM_X_SWING_TYPE value)
{
    return ToSeriesMode(value);
}

//
// END Swing Types ...
//

//
// START Period Method ...
//

//
// Period Calculation Method ...
enum ENUM_X_PERIOD_METHOD
{
    X_PERIOD_NONE,    // None
    X_PERIOD_AUTO,    // Auto Select
    X_PERIOD_MANUALLY // Manually
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_PERIOD_METHOD member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_PERIOD_METHOD value)
{
    //
    bool result = false;

    //
    result =
        value != NULL &&
        value != X_PERIOD_NONE;

    //
    return result;
}
bool IsXValid(ENUM_X_PERIOD_METHOD value)
{
    return IsValid(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_PERIOD_METHOD member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_PERIOD_METHOD value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_PERIOD_NONE:
        result = "None";
        break;

    //
    case X_PERIOD_AUTO:
        result = "Auto Select";
        break;

    //
    case X_PERIOD_MANUALLY:
        result = "Manually";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_PERIOD_METHOD value)
{
    return ToString(value);
}

//
// END Period Method ...
//

//
// START Market Cycles ...
//

//
// Market Cycles ...
enum ENUM_X_MARKET_CYCLES
{
    X_MARKET_CYCLE_UNKNOWN, // Unknown Cycle
    X_MARKET_CYCLE_SHORT,   // Short Cycle
    X_MARKET_CYCLE_MEDIUM,  // Medium Cycle
    X_MARKET_CYCLE_LONG,    // Long Cycle
    X_MARKET_CYCLE_HIND,    // Hind Cycle
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_MARKET_CYCLES member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_MARKET_CYCLES value)
{
    //
    bool result = false;

    //
    result =
        //
        value != NULL &&
        value != X_MARKET_CYCLE_UNKNOWN
        //
        ;

    //
    return result;
}
bool IsXValid(ENUM_X_MARKET_CYCLES value)
{
    return IsValid(value);
}
bool IsSpecifiedValid(ENUM_X_MARKET_CYCLES value)
{
    return IsValid(value);
}

/**
 * Converts an String to it's related Market Cycle ...
 *
 * @param  value: string ...
 *
 * @return ( ENUM_X_MARKET_CYCLES )
 */
ENUM_X_MARKET_CYCLES ToCycle(string value)
{
    //
    ENUM_X_MARKET_CYCLES result = X_MARKET_CYCLE_UNKNOWN;

    //
    ENUM_X_MARKET_CYCLES cycles[];
    int count = GetAllMarketCycles(cycles);
    for (int i = 0; i < count; i++)
    {
        //
        ENUM_X_MARKET_CYCLES iCycle = cycles[i];
        string iStr = ToString(iCycle);

        //
        if (value == iStr)
        {
            //
            result = iCycle;
            break;
        }
    }

    //
    return result;
}
ENUM_X_MARKET_CYCLES ToXCycle(string value)
{
    return ToCycle(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_MARKET_CYCLES member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_MARKET_CYCLES value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_MARKET_CYCLE_UNKNOWN:
        result = "Unknown Cycle";
        break;

    //
    case X_MARKET_CYCLE_SHORT:
        result = "Short Cycle";
        break;

    //
    case X_MARKET_CYCLE_MEDIUM:
        result = "Medium Cycle";
        break;

    //
    case X_MARKET_CYCLE_LONG:
        result = "Long Cycle";
        break;

    //
    case X_MARKET_CYCLE_HIND:
        result = "Hind Cycle";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_MARKET_CYCLES value)
{
    return ToString(value);
}

/**
 * Retrieve All Availabled Market Cycles ...
 *
 * @param  cycles: ENUM_X_MARKET_CYCLES member collection ...
 *
 * @return ( int )
 */
int GetAllMarketCycles(ENUM_X_MARKET_CYCLES &cycles[])
{
    //
    ZeroMemory(cycles);
    ArrayResize(cycles, 0);

    //
    ArrayResize(cycles, ArraySize(cycles) + 1);
    cycles[ArraySize(cycles) - 1] = X_MARKET_CYCLE_UNKNOWN;

    //
    ArrayResize(cycles, ArraySize(cycles) + 1);
    cycles[ArraySize(cycles) - 1] = X_MARKET_CYCLE_SHORT;

    //
    ArrayResize(cycles, ArraySize(cycles) + 1);
    cycles[ArraySize(cycles) - 1] = X_MARKET_CYCLE_MEDIUM;

    //
    ArrayResize(cycles, ArraySize(cycles) + 1);
    cycles[ArraySize(cycles) - 1] = X_MARKET_CYCLE_LONG;

    //
    ArrayResize(cycles, ArraySize(cycles) + 1);
    cycles[ArraySize(cycles) - 1] = X_MARKET_CYCLE_HIND;

    //
    int result = ArraySize(cycles);

    //
    return result;
}

//
// END Market Cycles ...
//

//
// START Week Days ...
//

//
// Week Days ...
enum ENUM_X_WEEK_DAYS
{
    X_WEEK_DAY_NONE = -1,     // None
    X_WEEK_DAY_SUNDAY = 0,    // Sunday
    X_WEEK_DAY_MONDAY = 1,    // Monday
    X_WEEK_DAY_TUESDAY = 2,   // Tuesday
    X_WEEK_DAY_WEDNESDAY = 3, // Wednesday
    X_WEEK_DAY_THURSDAY = 4,  // Thursday
    X_WEEK_DAY_FRIDAY = 5,    // Friday
    X_WEEK_DAY_SATURDAY = 6,  // Saturday
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_WEEK_DAYS member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_WEEK_DAYS value)
{
    //
    bool result = false;

    //
    result =
        //
        value != NULL &&
        value != X_WEEK_DAY_NONE
        //
        ;

    //
    return result;
}
bool IsXValid(ENUM_X_WEEK_DAYS value)
{
    return IsValid(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_WEEK_DAYS member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_WEEK_DAYS value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_WEEK_DAY_NONE:
        result = "None";
        break;

    //
    case X_WEEK_DAY_SUNDAY:
        result = "Sunday";
        break;

        //
    case X_WEEK_DAY_MONDAY:
        result = "Monday";
        break;

    //
    case X_WEEK_DAY_TUESDAY:
        result = "Tuesday";
        break;

    //
    case X_WEEK_DAY_WEDNESDAY:
        result = "Wednesday";
        break;

        //
    case X_WEEK_DAY_THURSDAY:
        result = "Thursday";
        break;

    //
    case X_WEEK_DAY_FRIDAY:
        result = "Friday";
        break;

    //
    case X_WEEK_DAY_SATURDAY:
        result = "Saturday";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_WEEK_DAYS value)
{
    return ToString(value);
}

//
// END Week Days ...
//

//
// START Ma Methods ...
//

//
// Ma Methods ...
enum ENUM_X_MA_METHOD
{
    X_MA_MODE_NONE, // None
    X_MA_MODE_SMA,  // SMA
    X_MA_MODE_EMA,  // EMA
    X_MA_MODE_LWMA, // LWMA
    X_MA_MODE_SMMA, // SMMA
};

//
// Extensions ...

/**
 * Validate ...
 *
 * @param  value: ENUM_X_MA_METHOD member ...
 *
 * @return ( bool )
 */
bool IsValid(ENUM_X_MA_METHOD value)
{
    //
    bool result = false;

    //
    result =
        //
        value != NULL &&
        value != X_MA_MODE_NONE
        //
        ;

    //
    return result;
}
bool IsXValid(ENUM_X_MA_METHOD value)
{
    return IsValid(value);
}

/**
 * Converts To String ...
 *
 * @param  value: ENUM_X_MA_METHOD member ...
 *
 * @return ( string )
 */
string ToString(ENUM_X_MA_METHOD value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_MA_MODE_NONE:
        result = "None";
        break;

    //
    case X_MA_MODE_SMA:
        result = "SMA";
        break;

    //
    case X_MA_MODE_EMA:
        result = "EMA";
        break;

    //
    case X_MA_MODE_LWMA:
        result = "LWMA";
        break;

    //
    case X_MA_MODE_SMMA:
        result = "SMMA";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_MA_METHOD value)
{
    return ToString(value);
}

/**
 * Converts MA Method ...
 **/
ENUM_MA_METHOD ToXMaMethod(ENUM_X_MA_METHOD value)
{
    //
    ENUM_MA_METHOD result = MODE_SMA;

    //
    if (!IsXValid(value))
    {
        return result;
    }

    //
    switch (value)
    {
    //
    case X_MA_MODE_EMA:
        result = MODE_EMA;
        break;

    //
    case X_MA_MODE_SMA:
        result = MODE_SMA;
        break;

    //
    case X_MA_MODE_LWMA:
        result = MODE_LWMA;
        break;

    //
    case X_MA_MODE_SMMA:
        result = MODE_SMMA;
        break;
    }

    //
    return result;
}
ENUM_X_MA_METHOD ToXMaMethod(ENUM_MA_METHOD value)
{
    //
    ENUM_X_MA_METHOD result = X_MA_MODE_NONE;

    //
    switch (value)
    {
    //
    case MODE_EMA:
        result = X_MA_MODE_EMA;
        break;

    //
    case MODE_SMA:
        result = X_MA_MODE_SMA;
        break;

    //
    case MODE_LWMA:
        result = X_MA_MODE_LWMA;
        break;

    //
    case MODE_SMMA:
        result = X_MA_MODE_SMMA;
        break;
    }

    //
    return result;
}

//
// END Ma Methods ...
//

//
// START XPivots ...
//

//
enum ENUM_X_PIVOT_TYPE
{
    X_PIVOT_TYPE_NONE = 0, // None
    X_PIVOT_TYPE_PEAK = 1, // PEAK
    X_PIVOT_TYPE_VALE = 2, // VALE
};

/**
 * Validate ...
 *
 * @param  value: ENUM_X_PIVOT_TYPE ...
 *
 * @return ( bool )
 */
bool IsXValid(ENUM_X_PIVOT_TYPE value)
{
    //
    bool result = false;

    //
    result = value != X_PIVOT_TYPE_NONE;

    //
    return result;
}

/**
 * Check Specified Type of Pivot is Peak or not ...
 *
 * @param  value: ENUM_X_PIVOT_TYPE, Specified Type ...
 *
 * @return ( bool )
 */
bool IsXPeak(ENUM_X_PIVOT_TYPE value)
{
    //
    bool result = false;

    //
    result = IsXValid(value) &&
             value == X_PIVOT_TYPE_PEAK;

    //
    return result;
}

/**
 * Check Specified Type of Pivot is Vale or not ...
 *
 * @param  value: ENUM_X_PIVOT_TYPE, Specified Type ...
 *
 * @return ( bool )
 */
bool IsXVale(ENUM_X_PIVOT_TYPE value)
{
    //
    bool result = false;

    //
    result = IsXValid(value) &&
             value == X_PIVOT_TYPE_VALE;

    //
    return result;
}

/**
 * Represent Specified ZigZag Point Type as String ...
 *
 * @param  value: ENUM_X_PIVOT_TYPE ...
 *
 * @return ( string )
 */
string ToXString(ENUM_X_PIVOT_TYPE value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_PIVOT_TYPE_NONE:
        result = "XNONE";
        break;

    //
    case X_PIVOT_TYPE_PEAK:
        result = "XPEAK";
        break;

    //
    case X_PIVOT_TYPE_VALE:
        result = "XVALE";
        break;
    }

    //
    return result;
    // return EnumToString(value);
}

//
// END XPivots ...
//

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-guard.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Seriallize Library
// ---------------------------------------
// Name: XGuardLib
// Description: All models related to Guard ...
//
//
// 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

//
// Imports ...
#include "../Libraries/x-saherelm.common.lib.mq5"

//
// Enumeration ...

//
// Guard Actions ...
enum ENUM_X_GUARD_ACTIONS
{
    X_GUARD_ACTION_NONE,                   // Nothing to Do
    X_GUARD_ACTION_CLOSE,                  // Close Specified Position
    X_GUARD_ACTION_CLOSE_ALL,              // Close All Positions
    X_GUARD_ACTION_CLOSE_LONGS,            // Close All Long Positions
    X_GUARD_ACTION_CLOSE_SHORTS,           // Close All Short Position
    X_GUARD_ACTION_CLOSE_IN_LOSTS,         // Close All In Lost Position
    X_GUARD_ACTION_CLOSE_IN_PROFITS,       // Close All In Profit Position
    X_GUARD_ACTION_CLOSE_IN_LOST_LONGS,    // Close All In Lost Long Position
    X_GUARD_ACTION_CLOSE_IN_PROFIT_LONGS,  // Close All In Profit Long Position
    X_GUARD_ACTION_CLOSE_IN_LOST_SHORTS,   // Close All In Lost Short Position
    X_GUARD_ACTION_CLOSE_IN_PROFITS_SHORT, // Close All In Profit Short Position
    X_GUARD_ACTION_PARTIAL_CLOSE,          // Partial Close Specified Position
    X_GUARD_ACTION_TRAIL_STOP,             // Trail Stop of Specified Position
    X_GUARD_ACTION_TRAIL_TARGET,           // Trail Target of Specified Position
    X_GUARD_ACTION_HEDGE,                  // Hedge Specified Positions
};

//
// Definitions ...

//
// Model a Guard Action ...
struct XGuard
{
    //
    // Props ...
    ENUM_X_GUARD_ACTIONS action;
    datetime time;

    //
    string symbol;
    string provider;
    ENUM_TIMEFRAMES period;

    //
    // Specified Position ...
    ulong ticket;

    //
    // Partial Close ...
    double volumeMultiplier;

    //
    // SL Trial ...
    double sl;

    //
    // TP Trial ...
    double tp;

    //
    bool force;

    //
    // Constructor ...
    XGuard()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        time = NULL;
        action = X_GUARD_ACTION_NONE;

        //
        symbol = NULL;
        period = NULL;
        provider = NULL;

        //
        ticket = 0;

        //
        volumeMultiplier = 0;

        //
        sl = 0;
        tp = 0;

        //
        force = false;

        //
        ZeroMemory(this);
    }

    /**
     * Validate ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(time) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period) &&
            IsSpecifiedValid(action) &&
            IsSpecifiedValid(provider)
            //
            ;

        if (!result)
        {
            return result;
        }

        //
        // Validate Model Based On Specified Guard Actions ...

        //
        return result;
    }
};

//
// Extensions ...

//
bool IsValid(ENUM_X_GUARD_ACTIONS item)
{
    //
    bool result = false;

    //
    result = item != X_GUARD_ACTION_NONE;

    //
    return result;
}
bool IsSpecifiedValid(ENUM_X_GUARD_ACTIONS item)
{
    return IsValid(item);
}

/**
 * Add Guard to Collection ...
 *
 * @param  guard: XGuard instance ...
 * @param  guards: XGuard instance Collection ...
 *
 * @return ( int )
 */
int AddGuard(
    XGuard &guard,
    XGuard &guards[] //
)
{
    //
    int result = 0;

    //
    if (!guard.IsValid())
    {
        return result;
    }

    //
    AddRef(
        guard,
        guards //
    );

    //
    result = ArraySize(guards);

    //
    return result;
}


### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-ohcl.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Common Library
// --------------------------------------
// Name: XOHCLLib
// Description: provide all commonly used functions ...
//
//
// 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.extensions.lib.mq5"

//
// Describe an Specific Candle ...
struct XOHCL
{
    //
    // Props ...

    //
    double high;
    double open;
    double close;
    double low;
    long volume;
    double spread;

    //
    string symbol;
    datetime time;
    ENUM_TIMEFRAMES period;

    //
    // Constructor ...
    XOHCL()
    {
        Clean();
    }

    //
    // Init a Bar ...
    // Using Bar Index ...
    bool Init(
        string mSymbol = NULL,          // Trading Symbol
        ENUM_TIMEFRAMES mPeriod = NULL, // Trading Period
        int barIndex = 0                // Bar Index
    )
    {
        //
        mSymbol = NormalizeSymbol(mSymbol);
        mPeriod = NormalizePeriod(mPeriod);

        //
        symbol = mSymbol;
        period = mPeriod;

        //
        time = iTime(
            mSymbol,
            mPeriod,
            barIndex //
        );

        //
        open = iOpen(
            mSymbol,
            mPeriod,
            barIndex //
        );
        open = NormalizePrice(open, mSymbol);

        //
        close = iClose(
            mSymbol,
            mPeriod,
            barIndex //
        );
        close = NormalizePrice(close, mSymbol);

        //
        high = iHigh(
            mSymbol,
            mPeriod,
            barIndex //
        );
        high = NormalizePrice(high, mSymbol);

        //
        low = iLow(
            mSymbol,
            mPeriod,
            barIndex //
        );
        low = NormalizePrice(low, mSymbol);

        //
        spread = iSpread(
            mSymbol,
            mPeriod,
            barIndex //
        );
        spread = NormalizePrice(spread, mSymbol);

        //
        volume = iTickVolume(
            mSymbol,
            mPeriod,
            barIndex //
        );

        //
        bool result = IsValid();

        //
        return result;
    }

    //
    // Init a Bar ...
    // Using Bar Time ...
    bool Init(
        string mSymbol = NULL,          // Trading Symbol
        ENUM_TIMEFRAMES mPeriod = NULL, // Trading Period
        datetime mTime = NULL           // Bar Time
    )
    {
        //
        mTime = NormalizeTime(mTime);

        //
        int barIndex = iBarShift(
            mSymbol,
            mPeriod,
            mTime,
            false //
        );

        //
        bool result = Init(
            mSymbol,
            mPeriod,
            barIndex
            //
        );

        //
        return result;
    }

    //
    // Tools ...

    //
    void Clean()
    {
        //
        low = 0;
        high = 0;
        open = 0;
        close = 0;
        spread = 0;
        volume = 0;

        //
        time = NULL;
        symbol = NULL;
        period = NULL;

        //
        ZeroMemory(this);
    }

    //
    // Validate Bar ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            low > 0 &&
            high > 0 &&
            open > 0 &&
            close > 0 &&
            volume > 0
            //
            // TODO: Fix in Some Cases for Zero Spread ...
            // spread > 0 &&
            //
            ;

        //
        return result;
    }

    //
    int TotalBars()
    {
        //
        int result = 0;

        //
        result = iBars(
            symbol,
            period //
        );

        //
        return result;
    }

    //
    // Calculate X_PRICE Specified Type ...
    double GetPrice(ENUM_X_PRICE mPType)
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        return GetAppliedPrice(
            mPType,
            open,
            high,
            low,
            close //
        );
    }

    //
    // Retrieve Price base On Series Mode ...
    double GetPrice(ENUM_SERIESMODE mMode)
    {
        //
        ENUM_X_PRICE mType = ToXPrice(mMode);

        //
        return GetPrice(mType);
    }

    //
    // Calculate Applied Price ...
    double GetPrice(ENUM_APPLIED_PRICE mPType)
    {
        //
        ENUM_X_PRICE mType = ToXPrice(mPType);

        //
        return GetPrice(mType);
    }

    //
    // Find Specific Price ...
    bool FindHigherPriceBar(
        XOHCL &bar,
        double price,
        ENUM_X_PRICE _type = X_PRICE_HIGH //
    )
    {
        //
        bool result = false;

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        int totalBars = TotalBars();
        result = IsValidSize(totalBars);
        if (!result)
        {
            return result;
        }

        //
        int start = Index();
        int maxAllowedLoopback = totalBars / 5;
        for (int i = start; i < maxAllowedLoopback; i++)
        {
            //
            result = bar.Init(
                symbol,
                period,
                i //
            );

            //
            if (!result)
            {
                break;
            }

            //
            double iPrice = bar.GetPrice(_type);

            //
            result =
                //
                iPrice > 0 &&
                iPrice > price
                //
                ;
            if (result)
            {
                break;
            }
        }

        //
        if (!result)
        {
            bar.Clean();
        }

        //
        return result;
    }

    //
    // Find Specific Price ...
    bool FindLowerPriceBar(
        XOHCL &bar,
        double price,
        ENUM_X_PRICE _type = X_PRICE_HIGH //
    )
    {
        //
        bool result = false;

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        int totalBars = TotalBars();
        result = IsValidSize(totalBars);
        if (!result)
        {
            return result;
        }

        //
        int start = Index();
        int maxAllowedLoopback = totalBars / 5;
        for (int i = start; i < maxAllowedLoopback; i++)
        {
            //
            result = bar.Init(
                symbol,
                period,
                i //
            );

            //
            if (!result)
            {
                break;
            }

            //
            double iPrice = bar.GetPrice(_type);

            //
            result =
                //
                iPrice > 0 &&
                iPrice < price
                //
                ;
            if (result)
            {
                break;
            }
        }

        //
        if (!result)
        {
            bar.Clean();
        }

        //
        return result;
    }

    //
    // Calculate up Price ...
    double GetUp()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = MathMax(open, close);

        //
        return result;
    }

    //
    // Calculate Down Price ...
    double GetDown()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = MathMin(open, close);

        //
        return result;
    }

    //
    // Calculate Bar Body ...
    double GetBody()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetUp() - GetDown();

        //
        return result;
    }

    //
    // Calculate Bar Range ...
    double GetRange()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = high - low;

        //
        return result;
    }

    //
    // Calculate Bar Shadows ...
    double GetShadows()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetRange() - GetBody();

        //
        return result;
    }

    //
    // Calculate Bar High Shadow ...
    double GetHighShadow()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            //
            high - GetUp()
            //
            ;

        //
        return result;
    }

    //
    // Calculate Bar Low Shadow ...
    double GetLowShadow()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            //
            GetDown() - low;
        //
        ;

        //
        return result;
    }

    //
    // Check Bar is Bullish ...
    bool IsBullish()
    {
        //
        bool result = false;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = open < close;

        //
        return result;
    }

    //
    // Check Bar is Bearish ...
    bool IsBearish()
    {
        //
        bool result = false;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = open > close;

        //
        return result;
    }

    //
    // Get Direction of Bar ...
    ENUM_X_DIRECTION GetDirection()
    {
        //
        ENUM_X_DIRECTION result = X_DIRECTION_NONE;

        //
        bool isBullish = IsBullish();
        bool isBearish = IsBearish();

        //
        result =
            isBullish && isBearish
                ? X_DIRECTION_ALL
            : !isBullish && !isBearish
                ? X_DIRECTION_NONE
            : isBullish
                ? X_DIRECTION_BULLISH
                : X_DIRECTION_BEARISH;

        //
        return result;
    }

    //
    // Find Next Bar Time ...
    datetime NextAt()
    {
        //
        datetime result = NULL;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        int prSeconds = PeriodSeconds(period);

        //
        result = time + prSeconds;

        //
        return result;
    }

    //
    // Find Prev Bar Time ...
    datetime BeforeOn()
    {
        //
        datetime result = NULL;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        int prSeconds = PeriodSeconds(period);

        //
        result = time - prSeconds;

        //
        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)
        {
            //
            result = time;
            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;
    }

    //
    bool BarAt(
        int index,
        XOHCL &bar //
    )
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        if (index < 0)
        {
            index = 0;
        }

        //
        result = bar.Init(
            symbol,
            period,
            index //
        );

        //
        return result;
    }

    //
    bool BarAt(
        datetime _time,
        XOHCL &bar //
    )
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        _time = NormalizeTime(_time);

        //
        result = bar.Init(
            symbol,
            period,
            _time //
        );

        //
        return result;
    }

    //
    bool BarIn(
        ENUM_TIMEFRAMES _period,
        XOHCL &bar //
    )
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        result = IsValid() &&
                 IsSpecifiedValid(_period);
        if (!result)
        {
            return result;
        }

        //
        int index = Index(_period);
        result = bar.Init(
            symbol,
            _period,
            index //
        );

        //
        return result;
    }

    //
    // Find Current Bar Index on Chart ...
    int Index()
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = iBarShift(
            symbol,
            period,
            time
            //
        );

        //
        return result;
    }

    //
    int Index(datetime value)
    {
        //
        int result = -1;

        //
        value = NormalizeTime(value);

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = iBarShift(
            symbol,
            period,
            value //
        );

        //
        return result;
    }

    //
    int Index(ENUM_TIMEFRAMES value)
    {
        //
        int result = -1;

        //
        result = IsValid() &&
                 IsSpecifiedValid(value);
        if (!result)
        {
            return result;
        }

        //
        result = iBarShift(
            symbol,
            value,
            time //
        );

        //
        return result;
    }

    //
    bool GetNextBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        int idx = Index();
        result = idx > 0;
        if (!result)
        {
            return result;
        }

        //
        result = bar.Init(
            this.symbol,
            this.period,
            idx - 1 //
        );

        //
        if (!result)
        {
            bar.Clean();
        }

        //
        return result;
    }

    //
    bool GetPreviousBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        int idx = Index();

        //
        result = bar.Init(
            this.symbol,
            this.period,
            idx + 1 //
        );

        //
        if (!result)
        {
            bar.Clean();
        }

        //
        return result;
    }

    //
    bool HasFiboPressure(
        ENUM_X_DIRECTION forDir,
        ENUM_X_FIBO_LEVELS level = X_FIBO_LEVEL_382 //
    )
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 HasDirection(forDir);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish = IsSpecifiedBullish(forDir);
        bool isBearish = IsSpecifiedBearish(forDir);

        //
        double fiboLevelValue = GetFibonacciLevel(
            high,
            low,
            level,
            forDir //
        );

        //
        isBullish =
            isBullish &&
            GetDown() > fiboLevelValue;
        isBearish =
            isBearish &&
            GetUp() < fiboLevelValue;

        //
        result = isBullish ||
                 isBearish;

        //
        return result;
    }

    //
    bool IsRejected(
        double price,
        ENUM_X_DIRECTION forDir,
        bool forceType = false,
        bool forcePressure = false //
    )
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 price > 0 &&
                 HasDirection(forDir);
        if (!result)
        {
            return result;
        }

        //
        bool isBullish =
            IsSpecifiedBullish(forDir) &&
            low < price &&
            GetDown() > price;

        //
        bool isBearish =
            IsSpecifiedBearish(forDir) &&
            high > price &&
            GetUp() < price;

        //
        if (forceType)
        {
            //
            isBullish =
                isBullish &&
                IsBullish();

            //
            isBearish =
                isBearish &&
                IsBearish();
        }

        //
        if (forcePressure)
        {
            //
            isBullish =
                isBullish &&
                HasFiboPressure(forDir);

            //
            isBearish =
                isBearish &&
                HasFiboPressure(forDir);
        }

        //
        result = isBullish ||
                 isBearish;

        //
        return result;
    }

    //
    bool IsBreaked(
        double price,
        ENUM_X_DIRECTION forDir,
        ENUM_X_FIBO_LEVELS fibLevel = X_FIBO_LEVEL_500,
        ENUM_X_BOUNDARY_PRICE boundaryType = X_BOUNDARY_PRICE_UP_DOWN //
    )
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 price > 0 &&
                 HasDirection(forDir) &&
                 IsSpecifiedValid(boundaryType);
        if (!result)
        {
            return result;
        }

        //
        bool isUpDownPriceType = boundaryType == X_BOUNDARY_PRICE_UP_DOWN;

        //
        bool isBullish =
            IsBullish() &&
            low < price &&
            high > price &&
            IsSpecifiedBullish(forDir);

        //
        bool isBearish =
            IsBearish() &&
            low < price &&
            high > price &&
            IsSpecifiedBearish(forDir);

        //
        result = isBullish ||
                 isBearish;
        if (result)
        {
            //
            // Calculate Range ...
            double range =
                isUpDownPriceType
                    ? GetBody()
                    : GetRange();

            //
            // Apply Edge Range ...
            double iReqRange =
                (range / 100) * (GetFiboLevelValue(fibLevel) * 100);

            //
            // Calculate Boundary Price ...
            double iBoundary =
                isUpDownPriceType
                    ? isBullish
                          ? GetUp()
                          : GetDown()
                : isBullish
                    ? high
                    : low;

            //
            // Detect Breaked Value ...

            //
            double breakedPrice =
                isBullish
                    ? (high - price)
                    : (price - low);

            //
            result =
                breakedPrice >= iReqRange;
        }

        //
        return result;
    }

    //
    // Find Highest Bar Index ...
    int FindHighestIndex(
        int mLength,          // Loopback ...
        ENUM_SERIESMODE mMode // Calculation mode
    )
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (mLength < 2)
        {
            mLength = 2;
        }

        //
        result = iHighest(
            symbol,
            period,
            mMode,
            mLength,
            Index());

        //
        return result;
    }

    //
    // Find Highest ...
    double FindHighest(
        int mLength,          // Loopback ...
        ENUM_SERIESMODE mMode // Calculation mode
    )
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        int idx = FindHighestIndex(
            mLength,
            mMode);
        if (idx <= -1)
        {
            return result;
        }

        //
        XOHCL iH;
        result = iH.Init(
            symbol,
            period,
            idx
            //
        );
        if (!result)
        {
            return result;
        }

        //
        result = iH.GetPrice(mMode);

        //
        return result;
    }

    /**
     * Detect Highest of Specified Price type in Market ...
     *
     * @param  index: souble, reference to hold detected Value Bar index ...
     * @param  mLength: int, Specified Loopback Length ...
     * @param  mPriceType: ENUM_X_PRICE, Specified Price type to Detect ...
     *
     * @return ( double )
     */
    double FindHighest(
        int &index,             // Index ...
        int mLength,            // Loopback ...
        ENUM_X_PRICE mPriceType // Price Type ...
    )
    {
        //
        double result = EMPTY_VALUE;

        //
        // Prepare ...
        index = -1;

        //
        // Normalize ...
        mLength = NormalizeInt(mLength, 1);

        //
        // Validate ...
        bool has = IsValid() &&
                   IsXValid(mPriceType);
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        int start = Index();
        int end = start + mLength;
        double iValue = EMPTY_VALUE;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            has = BarAt(i, iBar);
            if (!has)
            {
                continue;
            }

            //
            // Calculate Price ...
            iValue = iBar.GetPrice(mPriceType);

            //
            // Compare Price With Prev Detected ...
            has = !NotEmptyZero(result)
                      ? true
                      : iValue > result;
            if (has)
            {
                //
                index = i;
                result = iValue;
            }
        }

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    //
    // Find Highest ...
    double FindHighestUp(
        int mLength // Loopback ...
    )
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = FindHighest(mLength, MODE_OPEN);
        result = MathMax(mLength, FindHighest(mLength, MODE_CLOSE));

        //
        return result;
    }

    //
    // Find Lowest Bar Index ...
    int FindLowestIndex(
        int mLength,          // Loopback ...
        ENUM_SERIESMODE mMode // Calculation mode
    )
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (mLength < 2)
        {
            mLength = 2;
        }

        //
        result = iLowest(
            symbol,
            period,
            mMode,
            mLength,
            Index());

        //
        return result;
    }

    //
    // Find Lowest ...
    double FindLowest(
        int mLength,          // Loopback ...
        ENUM_SERIESMODE mMode // Calculation mode
    )
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        int idx = FindLowestIndex(
            mLength,
            mMode);
        if (idx <= -1)
        {
            return result;
        }

        //
        XOHCL iL;
        result = iL.Init(
            symbol,
            period,
            idx
            //
        );
        if (!result)
        {
            return result;
        }

        //
        result = iL.GetPrice(mMode);

        //
        return result;
    }

    /**
     * Detect Lowest of Specified Price type in Market ...
     *
     * @param  index: souble, reference to hold detected Value Bar index ...
     * @param  mLength: int, Specified Loopback Length ...
     * @param  mPriceType: ENUM_X_PRICE, Specified Price type to Detect ...
     *
     * @return ( double )
     */
    double FindLowest(
        int &index,             // Index ...
        int mLength,            // Loopback ...
        ENUM_X_PRICE mPriceType // Price Type ...
    )
    {
        //
        double result = EMPTY_VALUE;

        //
        // Prepare ...
        index = -1;

        //
        // Normalize ...
        mLength = NormalizeInt(mLength, 1);

        //
        // Validate ...
        bool has = IsValid() &&
                   IsXValid(mPriceType);
        if (!has)
        {
            return result;
        }

        //
        XOHCL iBar;
        int start = Index();
        int end = start + mLength;
        double iValue = EMPTY_VALUE;
        for (int i = start; i < end; i++)
        {
            //
            iBar.Clean();
            has = BarAt(i, iBar);
            if (!has)
            {
                continue;
            }

            //
            // Calculate Price ...
            iValue = iBar.GetPrice(mPriceType);

            //
            // Compare Price With Prev Detected ...
            has = !NotEmptyZero(result)
                      ? true
                      : iValue < result;
            if (has)
            {
                //
                index = i;
                result = iValue;
            }
        }

        //
        // Cleanup Resources ...
        iBar.Clean();

        //
        return result;
    }

    //
    // Find Lowest ...
    double FindLowesttDown(
        int mLength // Loopback ...
    )
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = FindLowest(mLength, MODE_OPEN);
        result = MathMax(mLength, FindLowest(mLength, MODE_CLOSE));

        //
        return result;
    }

    //
    // Swing Detection ...

    //
    int FindSwing(
        ENUM_X_SWING_TYPE type,
        int shoulders = 3 //
    )
    {
        //
        int result = -1;

        //
        // Validate ...
        bool isValid = IsValid() &&
                       IsXValid(type);
        if (!isValid)
        {
            return result;
        }

        //
        // Find Next Swing ...
        result = FindXSwing(
            type,
            symbol,
            period,
            Index(),
            shoulders //
        );

        //
        return result;
    }

    //
    int FindNextSwing(
        ENUM_X_SWING_TYPE type,
        int shoulders = 3 //
    )
    {
        //
        int result = -1;

        //
        // Validate ...
        bool isValid = IsValid() &&
                       IsXValid(type);
        if (!isValid)
        {
            return result;
        }

        //
        // Find Next Swing ...
        result = FindXSwing(
            type,
            symbol,
            period,
            Index() + 1,
            shoulders //
        );

        //
        return result;
    }

    //
    bool FindSwingBar(
        ENUM_X_SWING_TYPE type,
        XOHCL &swingBar,
        int shoulders = 3 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        swingBar.Clean();

        //
        // Validate ...
        result = IsValid() &&
                 IsXValid(type);
        if (!result)
        {
            return result;
        }

        //
        // Find Next Swing ...
        int idx = FindXSwing(
            type,
            symbol,
            period,
            Index(),
            shoulders //
        );
        result = IsValidIndex(idx);
        if (!result)
        {
            return result;
        }

        //
        result = swingBar.Init(
            symbol,
            period,
            idx //
        );

        //
        return result;
    }

    //
    bool FindNextSwingBar(
        ENUM_X_SWING_TYPE type,
        XOHCL &swingBar,
        int shoulders = 3 //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        swingBar.Clean();

        //
        // Validate ...
        result = IsValid() &&
                 IsXValid(type);
        if (!result)
        {
            return result;
        }

        //
        // Find Next Swing ...
        int idx = FindXSwing(
            type,
            symbol,
            period,
            Index() + 1,
            shoulders //
        );
        result = IsValidIndex(idx);
        if (!result)
        {
            return result;
        }

        //
        result = swingBar.Init(
            symbol,
            period,
            idx //
        );

        //
        return result;
    }

    //
    // Same Checkers ...

    //
    bool IsSameAs(XOHCL &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 IsSameTime(item) &&
                 IsSameMarket(item);

        //
        return result;
    }

    //
    bool IsSameTime(XOHCL &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 time == item.time;

        //
        return result;
    }

    //
    bool IsSameSymbol(XOHCL &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 symbol == item.symbol;

        //
        return result;
    }

    //
    bool IsSamePeriod(XOHCL &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 period == item.period;

        //
        return result;
    }

    //
    bool IsSameMarket(XOHCL &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 IsSameSymbol(item) &&
                 IsSamePeriod(item);

        //
        return result;
    }

    //
    // Other ...
    string GetTag(string prefix = "")
    {
        //
        string result = NULL;

        //
        result =
            (IsSpecifiedValid(prefix)
                 ? prefix + "_"
                 : "") +
            "XOHCL_" + symbol + "_" + ToXString(period) + "_" + ToFormatString(time);

        //
        return result;
    }

    //
    // Convert to String Representation ...
    string ToString()
    {
        //
        string result = "";

        //
        result = GetTypeName(this) +
                 "(O(" + ToXString(open) + "),H(" + ToXString(high) + "),C(" + ToXString(close) + "),L(" + ToXString(low) + "))";

        //
        return result;
    }
};

//
// Models ...

//
// Model a Bar Remains Time ...
struct XBarRemainsTime
{
    //
    int days;
    int hours;
    int minutes;
    int seconds;

    //
    // Constructor ...
    XBarRemainsTime()
    {
        Clean();
    }

    //
    // Always Calculate Current 0 Bar Timing ...
    bool Init(
        string mSymbol = NULL,         // Trading Symbol
        ENUM_TIMEFRAMES mPeriod = NULL // Trading Period
    )
    {
        //
        bool result = false;

        //
        mSymbol = NormalizeSymbol(mSymbol);
        mPeriod = NormalizePeriod(mPeriod);

        //
        int minuteSecoonds = 60;
        int hourSeconds = minuteSecoonds * 60;
        int daySeconds = 24 * hourSeconds;

        //
        int periodSeconds = PeriodSeconds(
            mPeriod);
        datetime startTime = GetBarTime(
            mSymbol,
            mPeriod,
            0
            //
        );

        //
        int secondsDiff = (int)startTime + periodSeconds - (int)TimeCurrent();

        //
        int mWorkingSecondsDiff = secondsDiff;
        if (mWorkingSecondsDiff > daySeconds)
        {
            //
            days = mWorkingSecondsDiff / daySeconds;
            mWorkingSecondsDiff -= days * daySeconds;
        }
        if (mWorkingSecondsDiff > hourSeconds)
        {
            //
            hours = mWorkingSecondsDiff / hourSeconds;
            mWorkingSecondsDiff -= hours * hourSeconds;
        }
        if (mWorkingSecondsDiff > minuteSecoonds)
        {
            //
            minutes = mWorkingSecondsDiff / minuteSecoonds;
            mWorkingSecondsDiff -= minutes * minuteSecoonds;
        }
        seconds = mWorkingSecondsDiff;

        //
        result = true;

        //
        return result;
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        days = 0;
        hours = 0;
        minutes = 0;
        seconds = 0;

        //
        ZeroMemory(this);
    }

    //
    // To String Representation ...
    string ToString(
        string dayId = "d",
        string hourId = "h",
        string minuteId = "m",
        string secondId = "s",
        string idSeparator = ": ",
        string separator = ", ",
        bool ignoreZeroValues = true
        //
    )
    {
        //
        string result = "";

        //
        // Days ...
        if (!ignoreZeroValues || days > 0)
        {
            result += dayId + idSeparator + (string)days + separator;
        }

        //
        // Hours ...
        if (!ignoreZeroValues || hours > 0)
        {
            //
            string strHour = ToXString(hours);
            if (StringLen(strHour) == 1)
            {
                strHour = "0" + strHour;
            }

            //
            result += hourId + idSeparator + strHour + separator;
        }

        //
        // Minutes ...
        if (!ignoreZeroValues || minutes > 0)
        {
            //
            string strMinute = ToXString(minutes);
            if (StringLen(strMinute) == 1)
            {
                strMinute = "0" + strMinute;
            }

            //
            result += minuteId + idSeparator + strMinute + separator;
        }

        //
        // Seconds ...
        if (!ignoreZeroValues || seconds > 0)
        {
            //
            string strSec = ToXString(seconds);
            if (StringLen(strSec) == 1)
            {
                strSec = "0" + strSec;
            }

            //
            result += secondId + idSeparator + strSec;
        }

        //
        return result;
    }
};

//
// Tracking Times ...
struct XTimeTracker
{
    //
    // Props ...
    int xMonth;
    int xDayOfWeek;
    int xForWeekDay;
    int xDay;
    int xHour;
    int xMinute;

    //
    // Constructor ...
    XTimeTracker()
    {
        Clean();
    }

    //
    // Tools ...

    //
    void Clean()
    {
        //
        xDay = -1;
        xHour = -1;
        xMonth = -1;
        xMinute = -1;
        xDayOfWeek = -1;
        xForWeekDay = -1;

        //
        ZeroMemory(this);
    }

    //
    // Detecting New Minute ...
    bool IsNewMinute()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check Houre ...
        result = timeStruct.min != xMinute;
        if (result)
        {
            xMinute = timeStruct.min;
        }

        //
        return result;
    }

    //
    // Detecting New Houre ...
    bool IsNewHour()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check Houre ...
        result = timeStruct.hour != xHour;
        if (result)
        {
            xHour = timeStruct.hour;
        }

        //
        return result;
    }

    //
    // Detecting New Day ...
    bool IsNewDay()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check Houre ...
        result = timeStruct.day_of_year != xDay;
        if (result)
        {
            xDay = timeStruct.day_of_year;
        }

        //
        return result;
    }

    //
    // Detecting New Week ...
    bool IsNewWeek()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check Week ...
        // Since Week Starts From Monday in Forex World ...
        // we Calculate Start of Week by 1-Monday ...
        result =
            xDayOfWeek == -1 &&
                    xForWeekDay == -1
                ? true
                : timeStruct.day_of_week == 1 &&
                      timeStruct.day_of_year > xForWeekDay;

        //
        xForWeekDay = timeStruct.day_of_year;
        xDayOfWeek = timeStruct.day_of_week;

        //
        return result;
    }

    //
    // Detecting End Of Week ...
    bool IsWeekEnd()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check WeekEnd ...
        // Since Week Starts From Monday in Forex World ...
        // we Calculate End of Week by 5-Friday ...
        result =
            xDayOfWeek != -1 &&
            timeStruct.day_of_week == 5 &&
            timeStruct.hour == 23 &&
            timeStruct.min == 59 &&
            timeStruct.sec == 59;

        //
        return result;
    }

    //
    // Detecting New Month ...
    bool IsNewMonth()
    {
        //
        bool result = false;

        //
        // Retrieve Current Time as Struct ...
        MqlDateTime timeStruct = GetCurrentTime();

        //
        // Check Houre ...
        result = timeStruct.mon != xMonth;
        if (result)
        {
            xMonth = timeStruct.mon;
        }

        //
        return result;
    }
};

//
// Tracking Bars on Specified Environment ...
struct XBarTracker
{
    //
    string symbol;          // Tracking Symbol
    ENUM_TIMEFRAMES period; // Tracking Time Frame

    //
    int lastTrackedBar; // Last Tracked Bar ...

    //
    bool waitsUntilNext; // Waits Until Next Bar ...

    //
    // Constructor ...
    XBarTracker()
    {
        Clean();
    }

    //
    // Initializer ...
    bool Init(
        string mSymbol = NULL,         // Trading Symbol
        ENUM_TIMEFRAMES mPeriod = NULL // Trading Period
    )
    {
        //
        bool result = false;

        //
        mSymbol = NormalizeSymbol(mSymbol);
        mPeriod = NormalizePeriod(mPeriod);

        //
        this.symbol = mSymbol;
        this.period = mPeriod;

        //
        this.lastTrackedBar = 0;

        //
        result = true;

        //
        return result;
    }

    //
    // Retrieve all Bars ...
    int CountBars()
    {
        //
        return iBars(
            symbol,
            period
            //
        );
    }

    //
    // Check if it's New Bar ...
    bool IsNewBar()
    {
        //
        bool result = false;

        //
        int currentBars = CountBars();

        //
        result = currentBars > lastTrackedBar;
        if (result)
        {
            lastTrackedBar = currentBars;
        }

        //
        return result;
    }

    //
    bool CanProcessBar()
    {
        //
        bool _isInTestMode = IsRunningOnTestMode();

        //
        bool isNewBar = IsNewBar();

        //
        bool result =
            _isInTestMode
                ? isNewBar
            : !waitsUntilNext
                ? true
                : isNewBar;
        if (result && waitsUntilNext)
        {
            waitsUntilNext = false;
        }

        //
        return result;
    }

    //
    void Waits()
    {
        this.waitsUntilNext = true;
    }

    //
    // Tools ...
    void Clean()
    {
        //
        symbol = NormalizeSymbol(NULL);
        period = NormalizePeriod(NULL);

        //
        ZeroMemory(this);
    }

    //
    bool GetBar(
        XOHCL &bar,
        int index = 0 //
    )
    {
        //
        bool result = false;

        //
        result = bar.Init(
            symbol,
            period,
            index //
        );

        //
        return result;
    }

    //
    bool GetBar(
        XOHCL &bar,
        datetime time = NULL //
    )
    {
        //
        bool result = false;

        //
        NormalizeTime(time);

        //
        int barIndex = iBarShift(
            symbol,
            period,
            time,
            false //
        );

        //
        result = barIndex >= 0;
        if (!result)
        {
            return result;
        }

        //
        result = bar.Init(
            symbol,
            period,
            barIndex //
        );

        //
        return result;
    }

    //
    int Index(datetime time = NULL)
    {
        //
        int result = 0;

        //
        NormalizeTime(time);

        //
        result = iBarShift(
            symbol,
            period,
            time,
            false //
        );

        //
        return result;
    }
};

//
// Extensions ...

//
// Extract Specific Range of Bars ...
// using Start Bar Index ...
int GetBars(
    XOHCL &bars[],                  // Hold Result
    string mSymbol = NULL,          // Trading Symbol
    ENUM_TIMEFRAMES mPeriod = NULL, // Trading Time Frame
    int from = 0,                   // Start Bar Index
    int count = 1                   // Number of Bars
)
{
    //
    int result = 0;

    //
    from = NormalizeInt(from);
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    int total = Bars(
        mSymbol,
        mPeriod //
    );
    if (from > total - 2)
    {
        from = total - 2;
    }

    //
    if (count < 0)
    {
        count = 1;
    }

    //
    int start = from;
    int end = start + count;
    if (end > total - 1)
    {
        end = total - 1;
    }

    //
    Clean(bars);

    //
    for (int i = start; i < end; i++)
    {
        //
        XOHCL iBar;
        bool isValid = iBar.Init(
            mSymbol,
            mPeriod,
            i //
        );

        //
        if (isValid)
        {
            //
            AddRef(
                iBar,
                bars //
            );
        }
    }

    //
    result = ArraySize(bars);

    //
    return result;
}

//
// Extract Specific Range of Bars ...
// using Start Bar Time ...
int GetBars(
    XOHCL &bars[],                  // Hold Result
    string mSymbol = NULL,          // Trading Symbol
    ENUM_TIMEFRAMES mPeriod = NULL, // Trading Time Frame
    datetime from = NULL,           // Start Bar Time
    int count = 1                   // Number of Bars
)
{
    //
    from = NormalizeTime(from);
    mSymbol = NormalizeSymbol(mSymbol);
    mPeriod = NormalizePeriod(mPeriod);

    //
    int barIndex = GetBarIndex(
        mSymbol,
        mPeriod,
        from //
    );

    //
    return GetBars(
        bars,
        mSymbol,
        mPeriod,
        barIndex,
        count //
    );
}

/**
 * Remove Specified Item from a Collection ...
 *
 * @param  item: Specified Item ...
 * @param  source: Specified Collection for Remove item from it ...
 *
 * @return ( bool )
 */
bool Remove(
    XOHCL &item,
    XOHCL &source[] //
)
{
    //
    bool result = false;

    //
    result =
        item.IsValid() &&
        HasChild(source);
    if (!result)
    {
        return result;
    }

    //
    int idx = -1;
    result = FindIndex(
        idx,
        item,
        source //
    );
    if (!result)
    {
        return result;
    }

    //
    result = ArrayRemove(
        source,
        idx,
        1 //
    );

    //
    return result;
}

/**
 * Remove Specified items from a Collection ...
 *
 * @param  items: Specified Items to Remove ...
 * @param  sources: Sepcified Collection for Removing items from it ...
 *
 * @return ( int )
 */
int Removes(
    XOHCL &items[],
    XOHCL &sources[] //
)
{
    //
    int result = 0;

    //
    bool has = HasChild(items) &&
               HasChild(sources);
    if (!has)
    {
        return result;
    }

    //
    int idx = -1;
    has = false;
    int count = ArraySize(items);
    for (int i = 0; i < count; i++)
    {
        //
        // Detect Item Index in Collection ...
        has = FindIndex(
            idx,
            items[i],
            sources //
        );
        if (has)
        {
            //
            // Remove Detected Index from Collection ...
            has = ArrayRemove(
                sources,
                idx,
                1 //
            );

            //
            if (has)
            {
                //
                // Count Removed Items ...
                result++;
            }
        }
    }

    //
    return result;
}

/**
 * Find Specified Item index in a Collection ...
 *
 * @param  index: int, reference to Hold Index if Exists ...
 * @param  item: Specified item to Detect ...
 * @param  source: Collection to Search for item ...
 *
 * @return ( bool )
 */
bool FindIndex(
    int &index,
    XOHCL &item,
    XOHCL &source[] //
)
{
    //
    bool result = false;

    //
    index = -1;

    //
    int count = ArraySize(source);
    result = item.IsValid() &&
             IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        bool isSame = item.IsSameAs(source[i]);
        if (isSame)
        {
            //
            index = i;
            break;
        }
    }

    //
    result = IsValidIndex(index);

    //
    return result;
}

/**
 * Add Specified Item to Specified Collection, if not Exists ...
 *
 * @param  item: Item to Add ...
 * @param  sources: Collection to Add item ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 *
 * @return ( bool )
 */
bool AddIfNotExists(
    XOHCL &item,
    XOHCL &sources[],
    int maxAllowed = 0 //
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    result = item.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Item Exists or not ...
    int idx = -1;
    bool isExists = FindIndex(
        idx,
        item,
        sources //
    );
    result = !isExists;
    if (!result)
    {
        return result;
    }

    //
    AddRef(
        item,
        sources //
    );

    //
    // Handle Cleanup Collection
    // if Max Reached ...
    if (result &&
        maxAllowed > 0)
    {
        //
        CleanupArray(
            sources,
            maxAllowed //
        );
    }

    //
    return result;
}

/**
 * Get Oldest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetOldest(
    XOHCL &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XOHCL selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.time > source[i].time;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Youngest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetYoungest(
    XOHCL &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XOHCL selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.time < source[i].time;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Lowest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetLowest(
    XOHCL &source[],
    ENUM_X_PRICE _priceType = X_PRICE_LOW //
)
{
    //
    int result = -1;

    //
    if (!IsXValid(_priceType))
    {
        _priceType = X_PRICE_LOW;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XOHCL selected;
    double iPrice = EMPTY_VALUE;
    double selectedPrice = EMPTY_VALUE;
    for (int i = 0; i < count; i++)
    {
        //
        iPrice = source[i].GetPrice(_priceType);
        selectedPrice = selected.GetPrice(_priceType);

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selectedPrice > iPrice;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Highest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  _priceType: Specified Price type ...
 *
 * @return ( int )
 */
int GetHighest(
    XOHCL &source[],
    ENUM_X_PRICE _priceType = X_PRICE_HIGH //
)
{
    //
    int result = -1;

    //
    if (!IsXValid(_priceType))
    {
        _priceType = X_PRICE_HIGH;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XOHCL selected;
    double iPrice = EMPTY_VALUE;
    double selectedPrice = EMPTY_VALUE;
    for (int i = 0; i < count; i++)
    {
        //
        iPrice = source[i].GetPrice(_priceType);
        selectedPrice = selected.GetPrice(_priceType);

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selectedPrice < iPrice;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Nearest Directional Bar ...
 *
 * @param  bar: XOHCL, reference to Specified Start Bar ...
 * @param  directionalBar: XOHCL, reference to hold Detected Bar ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Which Directional Bar looking for ...
 * @param  loopback: int, Loopback Length ...
 *
 * @return ( bool )
 */
bool GetDirectionalBar(
    XOHCL &bar,
    XOHCL &directionalBar,
    ENUM_X_DIRECTION forDir,
    int loopback = 1500 //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    directionalBar.Clean();

    //
    // Normalize ...
    loopback = NormalizeInt(loopback, 1);

    //
    // Validate ...
    result = bar.IsValid() &&
             HasDirection(forDir);
    if (!result)
    {
        return result;
    }

    //
    int start = bar.Index() + 1;
    int end = start + loopback;
    for (int i = start; i < end; i++)
    {
        //
        directionalBar.Clean();
        result = bar.BarAt(i, directionalBar);
        result = result &&
                 directionalBar.GetDirection() == forDir;
        if (result)
        {
            break;
        }

        //
        directionalBar.Clean();
    }

    //
    result = directionalBar.IsValid() &&
             forDir == directionalBar.GetDirection();

    //
    // Cleanup Resources ...
    if (!result)
    {
        directionalBar.Clean();
    }

    //
    return result;
}

/**
 * Get Nearest Breaker Bar of Specified Bar's Price ...
 *
 * @param  bar: XOHCL, reference to Specified Bar ...
 * @param  breaker: XOHCL, reference to hold Detected Breaker Bar ...
 * @param  priceType: ENUM_X_PRICE, which price type of Bar to Check to Break ...
 * @param  breakType: ENUM_X_PRICE, which price of Breaker Bar must Breke price ...
 * @param  breakDir: ENUM_X_DIRECTION, Specified Break Direction ...
 * @param  loopback: int, Max Allowed Loopback to Detect Breaker Bar ...
 *
 * @return ( bool )
 */
bool GetBreakerBar(
    XOHCL &bar,
    XOHCL &breaker,
    ENUM_X_PRICE priceType,
    ENUM_X_PRICE breakType,
    ENUM_X_DIRECTION breakDir,
    int loopback = 1500 //
)
{
    //
    // PriceType: which Applied Price of Specified Bar must to Check for Break ...

    //
    // BreakType: which Applied Price of breakerBar must Break price ...

    //
    // BreakDir:
    // -----------
    // Bullish: Price Must Break Applied Price from Down to Top ...
    // Bearish: Price must Break Applied Price from Top to Down ...

    //
    bool result = false;

    //
    // Prepare ...
    breaker.Clean();

    //
    // Normalize ...
    loopback = NormalizeInt(loopback, 1);

    //
    // Validate ...
    result = bar.IsValid() &&
             IsXValid(priceType) &&
             IsXValid(breakType) &&
             HasDirection(breakDir);
    if (!result)
    {
        return result;
    }

    //
    double iPrice = EMPTY_VALUE;
    bool isBullish = IsXBullish(breakDir);
    double price = bar.GetPrice(priceType);

    //
    int start = bar.Index();
    int end = start + loopback;
    for (int i = start; i < end; i++)
    {
        //
        breaker.Clean();
        iPrice = EMPTY_VALUE;

        //
        // Initialize Indexed Bar ...
        result = bar.BarAt(i, breaker);
        if (!result)
        {
            breaker.Clean();
            break;
        }

        //
        // Check Breake ...
        iPrice = breaker.GetPrice(breakType);
        result =
            isBullish
                ? iPrice > price
                : iPrice < price;
        if (result)
        {
            break;
        }
    }

    //
    result = breaker.IsValid();

    //
    // Cleanup Resources ...
    if (!result)
    {
        breaker.Clean();
    }

    //
    return result;
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-pivot.analysis.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Seriallize Library
// ---------------------------------------
// Name: XPOILib
// Description: All models related to POIs ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2025, SaherElm IT Center"
#property link "https://www.saherelm.ir"
#property version "1.00"
#property strict

//
// Imports ...
#include "../Libraries/x-saherelm.common.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"

/**
 * Model Specified Pivots Analysis ...
 **/
struct XPivotAnalysis
{
    //
    //  Props ...
    string name;            // Name ...
    string symbol;          // Symbol ...
    datetime time;          // Time ...
    XPivot pivots[];        // Pivots ...
    ENUM_TIMEFRAMES period; // Period ...

    //
    // Linear Regression Parameters ...
    double peakSlope;
    double valeSlope;
    double peakIntercept;
    double valeIntercept;

    //
    // Analysed Results ...

    //
    // Directions ...
    ENUM_X_DIRECTION dir; // Trending Direction ...
    ENUM_X_DIRECTION peakDir;
    ENUM_X_DIRECTION valeDir;

    //
    // Predicted Values ...
    double peakTo;
    double valeTo;
    double peakFrom;
    double valeFrom;

    //
    // Time Analysis ...
    datetime toTime;
    datetime fromTime;

    //
    // Support and Resistances ...
    double minorSupport;
    double majorSupport;
    double minorResistance;
    double majorResistance;

    //
    // Constructor ...
    XPivotAnalysis()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleaners ...

    /**
     * Cleanup Model ...
     */
    void Clean()
    {
        //
        time = NULL;
        name = NULL;
        symbol = NULL;
        period = NULL;
        SpecifiedClean(pivots);

        //
        CleanAnalysis();
        CleanLinearRegressionParameters();

        //
        ZeroMemory(this);
    }

    /**
     * Clean Analysis Props ...
     */
    void CleanAnalysis()
    {
        //
        toTime = NULL;
        fromTime = NULL;
        peakTo = EMPTY_VALUE;
        valeTo = EMPTY_VALUE;
        peakFrom = EMPTY_VALUE;
        valeFrom = EMPTY_VALUE;
        dir = X_DIRECTION_NONE;
        peakDir = X_DIRECTION_NONE;
        valeDir = X_DIRECTION_NONE;
        minorSupport = EMPTY_VALUE;
        majorSupport = EMPTY_VALUE;
        minorResistance = EMPTY_VALUE;
        majorResistance = EMPTY_VALUE;
    }

    /**
     * Clean Linear Regression Parameters ...
     */
    void CleanLinearRegressionParameters()
    {
        //
        peakSlope = EMPTY_VALUE;
        valeSlope = EMPTY_VALUE;
        peakIntercept = EMPTY_VALUE;
        valeIntercept = EMPTY_VALUE;
    }

    //
    // Initializer / Actions (s) ...

    /**
     * Initialize Model ...
     *
     * @param  _name: Name ...
     * @param  _symbol: Symbol ...
     * @param  _period: Period ...
     * @param  _pivots: Provided Pivots ...
     *
     * @return ( bool )
     */
    bool Init(
        string _name,
        string _symbol,
        ENUM_TIMEFRAMES _period,
        XPivot &_pivots[] //
    )
    {
        //
        bool result = false;

        //
        // Validate Args ...
        result =
            HasChild(_pivots) &&
            IsSpecifiedValid(_name) &&
            IsSpecifiedValid(_symbol) &&
            IsSpecifiedValid(_period);
        if (!result)
        {
            //
            Clean();
            return result;
        }

        //
        name = _name;
        symbol = _symbol;
        period = _period;
        Copy(
            _pivots,
            pivots //
        );
        time = TimeCurrent();

        //
        result = IsValid();
        if (!result)
        {
            Clean();
        }

        //
        // Do Calculations if Model is Valid ...
        if (result)
        {
            Update();
        }

        //
        return result;
    }

    /**
     * Fully Update Analysis  based on Pivots ...
     */
    void Update()
    {
        //
        bool has = false;

        //
        // Prepare ...
        CleanAnalysis();
        CleanLinearRegressionParameters();

        //
        // Validate ...
        has = IsValid() &&
              HasPivots();
        if (!has)
        {
            return;
        }

        //
        // Calculate Linear Regression Parameters ...
        has = CalculateLinearRegressionParameters();
        if (!has)
        {
            return;
        }

        //
        // Analyse ...
        has = Analyse();
        if (!has)
        {
            return;
        }
    }

    /**
     * Fully Update Analysis based on Pivots ...
     *
     * @param  _pivots: new Pivots Collection for Update ...
     */
    void Update(XPivot &_pivots[])
    {
        //
        bool has =
            IsValid() &&
            HasChild(_pivots);
        if (!has)
        {
            return;
        }

        //
        SpecifiedClean(pivots);
        Copy(
            _pivots,
            pivots //
        );

        //
        Update();
    }

    /**
     * Re Analyse Pivots ...
     *
     * @return ( bool )
     */
    bool Analyse()
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanAnalysis();

        //
        // Validate ...
        result = IsValid() &&
                 HasPivots() &&
                 HasLinearRegressionParameters();
        if (!result)
        {
            return result;
        }

        //
        // Do Analyse ...

        //
        bool has = false;

        //
        // Specified To and From Time ...

        //
        // To Time ...
        int toIDX = GetYoungest(pivots);
        if (IsValidIndex(toIDX))
        {
            //
            toTime = pivots[toIDX].time;
            toIDX = GetBarIndex(
                symbol,
                period,
                toTime //
            );
        }

        //
        // From Time ...
        int fromIDX = GetOldest(pivots);
        if (IsValidIndex(fromIDX))
        {
            //
            fromTime = pivots[fromIDX].time;
            fromIDX = GetBarIndex(
                symbol,
                period,
                fromTime //
            );
        }

        //
        // Validate To From times ...
        result =
            IsValidIndex(toIDX) &&
            IsValidIndex(fromIDX) &&
            IsSpecifiedValid(toTime) &&
            IsSpecifiedValid(fromTime);
        if (!result)
        {
            return result;
        }

        //
        // Predict Values ...

        //
        // Peaks ...

        //
        // Check Slope and Intercept Exists ...
        has = NotEmpty(peakSlope) &&
              NotEmpty(peakIntercept);
        if (has)
        {
            //
            // To ...
            peakTo = PredictValue(
                peakSlope,
                peakIntercept,
                toIDX //
            );

            //
            // From ...
            peakFrom = PredictValue(
                peakSlope,
                peakIntercept,
                fromIDX //
            );
        }

        //
        // Vales ...

        //
        // Check Slope and Intercept Exists ...
        has = NotEmpty(valeSlope) &&
              NotEmpty(valeIntercept);
        if (has)
        {
            //
            // To ...
            valeTo = PredictValue(
                valeSlope,
                valeIntercept,
                toIDX //
            );

            //
            // From ...
            valeFrom = PredictValue(
                valeSlope,
                valeIntercept,
                fromIDX //
            );
        }

        //
        // Check Directions ...

        //
        // Peak ...
        has = IsPeakAnalysed();
        if (has)
        {
            //
            peakDir =
                peakFrom < peakTo
                    ? X_DIRECTION_BULLISH
                : peakFrom > peakTo
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
        }

        //
        // Vale ...
        has = IsValeAnalysed();
        if (has)
        {
            //
            valeDir =
                valeFrom < valeTo
                    ? X_DIRECTION_BULLISH
                : valeFrom > valeTo
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
        }

        //
        // Check For Strong Type Trending ...
        has =
            IsPeakAnalysed() &&
            IsValeAnalysed() &&
            peakDir == valeDir;
        if (has)
        {
            dir = peakDir;
        }

        //
        // Calculate Support and Resistances ...

        //
        // Support ...
        ExtractSupport(
            minorSupport,
            majorSupport,
            pivots //
        );

        //
        // Resistance ...
        ExtractResistances(
            minorResistance,
            majorResistance,
            pivots //
        );

        //
        result = IsAnalysed();

        //
        // Cleanup Resources ...

        //
        return result;
    }

    /**
     * Re Calculate Linear Regression Parameters ...
     *
     * @return ( bool )
     */
    bool CalculateLinearRegressionParameters()
    {
        //
        bool result = false;

        //
        // Prepare ...
        CleanLinearRegressionParameters();

        //
        // Validate ...
        result = IsValid() &&
                 HasPivots();
        if (!result)
        {
            return result;
        }

        //
        // Make a Copy of Pivots ...
        XPivot tmpPivots[];
        Copy(
            pivots,
            tmpPivots //
        );

        //
        // Sort Pivots ...
        Sort(
            tmpPivots,
            X_SORT_BY_DATE,
            X_DIRECTION_BEARISH //
        );

        //
        // Do Calculations ...

        //
        double yData[];
        double xData[];
        bool has = false;

        //
        // Peaks ...

        //
        XPivot peaks[];
        int peaksCount = ExtractPivots(
            peaks,
            tmpPivots,
            X_PIVOT_TYPE_PEAK //
        );
        has = IsValidSize(peaksCount);
        if (has)
        {
            //
            // Extract X and Y axis Data ...
            ExtractPivotsData(
                xData,
                yData,
                peaks //
            );

            //
            // Calculating Slope and Intercept ...
            has = ArraySize(xData) >= 2 &&
                  ArraySize(yData) >= 2;
            if (has)
            {
                //
                CalculateSlopeAndIntercept(
                    peakSlope,
                    peakIntercept,
                    xData,
                    yData //
                );
            }
        }

        //
        // Vales ...

        //
        XPivot vales[];
        int valesCount = ExtractPivots(
            vales,
            tmpPivots,
            X_PIVOT_TYPE_VALE //
        );
        has = IsValidSize(valesCount);
        if (has)
        {
            //
            // Extract X and Y axis Data ...
            ExtractPivotsData(
                xData,
                yData,
                vales //
            );

            //
            // Calculating Slope and Intercept ...
            has = ArraySize(xData) >= 2 &&
                  ArraySize(yData) >= 2;
            if (has)
            {
                //
                CalculateSlopeAndIntercept(
                    valeSlope,
                    valeIntercept,
                    xData,
                    yData //
                );
            }
        }

        //
        // Validate Result ...
        result =
            IsValid() &&
            HasLinearRegressionParameters();

        //
        // Cleanup Resources ...
        SpecifiedClean(yData);
        SpecifiedClean(xData);
        SpecifiedClean(peaks);
        SpecifiedClean(vales);
        SpecifiedClean(tmpPivots);

        //
        return result;
    }

    //
    // Validator / Checker (s) ...

    /**
     * Validate Model ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            HasPivots() &&
            IsSpecifiedValid(time) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period);

        //
        return result;
    }

    /**
     * Check Model has Pivots or not ...
     *
     * @return ( bool )
     */
    bool HasPivots()
    {
        //
        bool result = false;

        //
        result = HasChild(pivots);

        //
        return result;
    }

    /**
     * Check Model is Analysed or not ...
     *
     * @return ( bool )
     */
    bool IsAnalysed()
    {
        //
        bool result = false;

        //
        result =
            IsPeakAnalysed() ||
            IsValeAnalysed();

        //
        return result;
    }

    /**
     * Check Peak Analysed or not ...
     *
     * @return ( bool )
     */
    bool IsPeakAnalysed()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            HasLinearRegressionParameters() &&
            (
                //
                NotEmpty(peakTo) &&
                NotEmpty(peakFrom) &&
                // HasDirection(peakDir) &&
                IsSpecifiedValid(toTime) &&
                IsSpecifiedValid(fromTime)
                //
            );

        //
        return result;
    }

    /**
     * Check Vale Analysed or not ...
     *
     * @return ( bool )
     */
    bool IsValeAnalysed()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            HasLinearRegressionParameters() &&
            (
                //
                NotEmpty(valeTo) &&
                NotEmpty(valeFrom) &&
                // HasDirection(valeDir) &&
                IsSpecifiedValid(toTime) &&
                IsSpecifiedValid(fromTime)
                //
            );

        //
        return result;
    }

    /**
     * Check Models Linear Regression Parameters Calculated or not ...
     *
     * @return ( bool )
     */
    bool HasLinearRegressionParameters()
    {
        //
        bool result = false;

        //
        result =
            (NotEmpty(peakSlope) &&
             NotEmpty(peakIntercept)) ||
            (NotEmpty(valeSlope) ||
             NotEmpty(valeIntercept));

        //
        return result;
    }

    /**
     * Check Analysis Has Trend or not ...
     *
     * @return ( bool )
     */
    bool HasTrend()
    {
        //
        bool result = false;

        //
        result = IsAnalysed() &&
                 HasDirection(dir);

        //
        return result;
    }

    /**
     * Check Analysis Has Bullish Trend or not ...
     *
     * @return ( bool )
     */
    bool HasBullishTrend()
    {
        //
        bool result = false;

        //
        result = HasTrend() &&
                 IsSpecifiedBullish(dir);

        //
        return result;
    }

    /**
     * Check Analysis Has Bearish Trend or not ...
     *
     * @return ( bool )
     */
    bool HasBearishTrend()
    {
        //
        bool result = false;

        //
        result = HasTrend() &&
                 IsSpecifiedBearish(dir);

        //
        return result;
    }

    /**
     * Check Market Has Support ...
     *
     * @return ( bool )
     */
    bool HasSupport()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            HasPivots() &&
            IsAnalysed() &&
            NotEmpty(minorSupport) &&
            NotEmpty(majorSupport);

        //
        return result;
    }

    /**
     * Check Market Has Resistance ...
     *
     * @return ( bool )
     */
    bool HasResistance()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            HasPivots() &&
            IsAnalysed() &&
            NotEmpty(minorResistance) &&
            NotEmpty(majorResistance);

        //
        return result;
    }

    //
    // Pivots ...

    /**
     * Extract all Peaks from Exists Pivots ...
     *
     * @param  dest: a Collection refrence to Hold Extracted Pivots ...
     *
     * @return ( int )
     */
    int ExtractPeaks(XPivot &dest[])
    {
        //
        int result = -1;

        //
        // Prepare ...
        SpecifiedClean(dest);

        //
        // Do Extraction ...
        result = ExtractPivots(
            dest,
            pivots,
            X_PIVOT_TYPE_PEAK //
        );

        //
        return result;
    }

    /**
     * Extract all Vales from Exists Pivots ...
     *
     * @param  dest: a Collection refrence to Hold Extracted Pivots ...
     *
     * @return ( int )
     */
    int ExtractVales(XPivot &dest[])
    {
        //
        int result = -1;

        //
        // Prepare ...
        SpecifiedClean(dest);

        //
        // Do Extraction ...
        result = ExtractPivots(
            dest,
            pivots,
            X_PIVOT_TYPE_VALE //
        );

        //
        return result;
    }

    /**
     * Sort Pivots ...
     *
     * @param  sortBy: ENUM_X_SORT_BY, Specified Sort Param ...
     * @param  sortDir: ENUM_X_DIRECTION, Specified Sort Direction ...
     */
    void SortPivots(
        ENUM_X_SORT_BY sortBy,
        ENUM_X_DIRECTION sortDir //
    )
    {
        //
        // Validate ...
        bool has = IsValid() &&
                   HasPivots();
        if (!has)
        {
            return;
        }

        //
        // Apply Sorting ...
        Sort(
            pivots,
            sortBy,
            sortDir //
        );
    }

    //
    // Boxing Tools ...

    /**
     * Generate Support Box if Has Strong Supports ...
     *
     * @param  box: a refrence XBoxZone to holds Generated result ...
     *
     * @return ( bool )
     */
    bool HasSupportBox(XBoxZone &box)
    {
        //
        bool result = false;

        //
        // Prepare ...
        box.Clean();

        //
        // Validate ...
        result =
            IsValid() &&
            IsAnalysed();
        if (!result)
        {
            return result;
        }

        //
        box.to = toTime;
        box.symbol = symbol;
        box.period = period;
        box.from = fromTime;

        //
        box.upper = minorSupport;
        box.lower = majorSupport;
        box.dir = X_DIRECTION_BULLISH;
        box.type = GetTag() + "_Support";

        //
        result = box.IsValid();
        if (!result)
        {
            box.Clean();
        }

        //
        return result;
    }

    /**
     * Generate Resistance Box if Has Strong Resistance ...
     *
     * @param  box: a refrence XBoxZone to holds Generated result ...
     *
     * @return ( bool )
     */
    bool HasResistanceBox(XBoxZone &box)
    {
        //
        bool result = false;

        //
        // Prepare ...
        box.Clean();

        //
        // Validate ...
        result =
            IsValid() &&
            IsAnalysed();
        if (!result)
        {
            return result;
        }

        //
        box.to = toTime;
        box.symbol = symbol;
        box.period = period;
        box.from = fromTime;

        //
        box.upper = majorResistance;
        box.lower = minorResistance;
        box.dir = X_DIRECTION_BEARISH;
        box.type = GetTag() + "_Resistance";

        //
        result = box.IsValid();
        if (!result)
        {
            box.Clean();
        }

        //
        return result;
    }

    //
    // Detecting ...

    /**
     * Check Two Model is Same or not ...
     *
     * @param  item: Specified Model for Compare ...
     *
     * @return ( bool )
     */
    bool IsSameAs(XPivotAnalysis &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 IsSameMarketAs(item) &&
                 name == item.name &&
                 time == item.time;

        //
        return result;
    }

    /**
     * Check Two Model Has Same Symbol or not ...
     *
     * @param  item: Specified Model for Compare ...
     *
     * @return ( bool )
     */
    bool IsSameSymbolAs(XPivotAnalysis &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 symbol == item.symbol;

        //
        return result;
    }

    /**
     * Check Two Model Has Same Period or not ...
     *
     * @param  item: Specified Model for Compare ...
     *
     * @return ( bool )
     */
    bool IsSamePeriodAs(XPivotAnalysis &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 period == item.period;

        //
        return result;
    }

    /**
     * Check Two Model Has Same Symbol/Period or not ...
     *
     * @param  item: Specified Model for Compare ...
     *
     * @return ( bool )
     */
    bool IsSameMarketAs(XPivotAnalysis &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid() &&
                 IsSameSymbolAs(item) &&
                 IsSamePeriodAs(item);

        //
        return result;
    }

    //
    // Logging ...

    /**
     * Extract Specified Token of Struct ...
     *
     * @return ( string )
     */
    string GetTag()
    {
        return name + GetToken(this);
    }

    /**
     * Represent an String Unique Identifier for Chart Objects ...
     *
     * @return ( string )
     */
    string GetObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period);

        //
        return result;
    }

    /**
     * Represent an String Unique Identifier for Upper Trend Chart Objects ...
     *
     * @return ( string )
     */
    string GetUpperObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period) + "_UPPER";

        //
        return result;
    }

    /**
     * Represent an String Unique Identifier for Lower Trend Chart Objects ...
     *
     * @return ( string )
     */
    string GetLowerObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period) + "_LOWER";

        //
        return result;
    }

    /**
     * Represent an String Unique Identifier for Support Box Chart Objects ...
     *
     * @return ( string )
     */
    string GetSupportBoxObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period) + "_SUPPORT";

        //
        return result;
    }

    /**
     * Represent an String Unique Identifier for Resistance BOX Chart Objects ...
     *
     * @return ( string )
     */
    string GetResistanceBoxObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period) + "_RESISTANCE";

        //
        return result;
    }

    /**
     * Represent as String ...
     *
     * @param  spacer: string, How to Represent Spaces ...
     *
     * @return ( string )
     */
    string GetMessage(string spacer = NULL)
    {
        //
        string result = "";

        //
        // Normalize Spacer ...
        if (!IsSpecifiedValid(spacer))
        {
            spacer = " ";
        }

        //
        // Validate ...
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            "==========================" + spacer +
            GetTag() + ":" + spacer +
            "==========================" + spacer +
            "Symbol: " + symbol + spacer +
            "Period: " + ToXString(period) + spacer +
            (!HasTrend()
                 ? ""
                 : "Direction: " + ToXString(dir) + spacer) +
            "Time: " + ToXString(time) + spacer;

        //
        return result;
    }

    //
};

/**
 * a Model to  Describe Specified Bar Pattern's Analysis ...
 **/
struct XPatternAnalysis
{
    //
    // Props ...
    XOHCL bar;                      // Specified Bar ...
    XBoxZone ob;                    // OrderBlock if Exists ...
    XBoxZone fvg;                   // Fair Value Gap if Exists ...
    datetime time;                  // Analysis Time ...
    XOHCL flagSwing;                // Flag Patterns Swing Bar if Exists ...
    XBoxZone support;               // Bar's Support if Exists ...
    XBoxZone resistance;            // Bar's Resistance if Exists ...
    ENUM_X_BAR_PATTERN patterns[];  // All Bars Exists Patterns ...
    ENUM_X_DIRECTION patternsDir[]; // Each Exists Patterns Directions ...

    //
    // Constructor ...
    XPatternAnalysis()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup Model ...
     */
    void Clean()
    {
        //
        ob.Clean();
        bar.Clean();
        fvg.Clean();
        time = NULL;
        support.Clean();
        XClean(patterns);
        flagSwing.Clean();
        resistance.Clean();
        XClean(patternsDir);

        //
        ZeroMemory(this);
    }

    /**
     * Validate Model ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result = bar.IsValid() &&
                 IsXValid(time);

        //
        return result;
    }

    //
    // Pattern Actions ...

    /**
     * Count Directional Patterns ...
     *
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
     *
     * @return ( int )
     */
    int Count(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int result = 0;

        //
        // Validate ...
        bool has = IsValid() &&
                   HasChild(patternsDir);
        if (!has)
        {
            return result;
        }

        //
        int count = ArraySize(patternsDir);
        if (!HasDirection(forDir))
        {
            //
            result = count;
            return result;
        }

        //
        for (int i = 0; i < count; i++)
        {
            //
            has = forDir == patternsDir[i];
            if (has)
            {
                result++;
            }
        }

        //
        return result;
    }

    //
    // Detectors ...

    bool IsHigh()
    {
        //
        bool result = false;

        //
        result = HasPattern(X_BAR_PATTERN_HIGH);

        //
        return result;
    }

    bool IsLow()
    {
        //
        bool result = false;

        //
        result = HasPattern(X_BAR_PATTERN_LOW);

        //
        return result;
    }

    bool IsSupport()
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_SUPPORT //
        );

        //
        return result;
    }

    bool IsResistance()
    {
        //
        bool result = false;

        //
        result = HasPattern(X_BAR_PATTERN_RESISTANCE);

        //
        return result;
    }

    bool IsMomentum(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_MOMENTUM,
            forDir //
        );

        //
        return result;
    }

    bool IsEngulfed(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_ENGULFED,
            forDir //
        );

        //
        return result;
    }

    bool IsTrueGaped(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_TRUE_GAPED,
            forDir //
        );

        //
        return result;
    }

    bool IsPinned(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_PINNED,
            forDir //
        );

        //
        return result;
    }

    bool IsStar(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_STAR,
            forDir //
        );

        //
        return result;
    }

    bool IsPiercing(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_PIERCING,
            forDir //
        );

        //
        return result;
    }

    bool IsRising(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_RISING,
            forDir //
        );

        //
        return result;
    }

    bool IsFlag(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_FLAG,
            forDir //
        );

        //
        return result;
    }

    bool IsSignalKey(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_SIGNALKEY,
            forDir //
        );

        //
        return result;
    }

    bool IsOB(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_OB,
            forDir //
        );

        //
        return result;
    }

    bool IsFVG(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = HasPattern(
            X_BAR_PATTERN_FVG,
            forDir //
        );

        //
        return result;
    }

    //
    // Getters ...

    bool GetLow(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_LOW;
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_BULLISH;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetHigh(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_HIGH;
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_BEARISH;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetSupport(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_SUPPORT;
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_BULLISH;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetResistance(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_RESISTANCE;
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_BEARISH;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetOB(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_OB;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetFVG(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_FVG;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetMomentum(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_MOMENTUM;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetEngulfed(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_ENGULFED;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetPinned(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_PINNED;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetTrueGaped(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_TRUE_GAPED;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetStar(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_STAR;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetFlag(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_FLAG;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetRising(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_RISING;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetPiercing(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_PIERCING;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    bool GetSignalKey(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        ENUM_X_BAR_PATTERN _pattern = X_BAR_PATTERN_SIGNALKEY;

        //
        return GetPattern(
            _bar,
            _pivot,
            _zone,
            _pattern,
            _patternDir //
        );
    }

    //
    // Common Tools ...

    /**
     * Add Specified Patterns to Model ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     * @param  patternDir: ENUM_X_DIRECTION, Specified Pattern Direction ...
     */
    void AddPattern(
        ENUM_X_BAR_PATTERN pattern,
        ENUM_X_DIRECTION patternDir //
    )
    {
        //
        bool has = IsXValid(pattern) &&
                   HasDirection(patternDir);
        if (!has)
        {
            return;
        }

        //
        Add(
            pattern,
            patterns //
        );

        //
        Add(
            patternDir,
            patternsDir //
        );
    }

    /**
     * Check Model Has Specified Pattern or not ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     * @param  patternDir: ENUM_X_DIRECTION, Specified Pattern Direction ...
     *
     * @return ( bool )
     */
    bool HasPattern(
        ENUM_X_BAR_PATTERN pattern,
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = Contains(
            pattern,
            patterns //
        );
        if (!result)
        {
            return result;
        }

        //
        if (HasDirection(forDir))
        {
            //
            int idx = FindIndex(
                pattern,
                patterns //
            );
            result =
                IsValidIndex(idx) &&
                forDir == patternsDir[idx];
        }

        //
        return result;
    }

    /**
     * Get Specified Pattern if Exists ...
     *
     * @param  _bar: XOHCL, Patterns Bar ...
     * @param  _pivot: XPivot, Patterns Pivot ...
     * @param  _zone: XBoxZone, Patterns Box ...
     * @param  _pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     * @param  _patternDir: ENUM_X_DIRECTION, Specified Pattern Direction ...
     * @param  _peakPriceType: ENUM_X_PRICE, Specified Peak Price Type ...
     * @param  _valePriceType: ENUM_X_PRICE, Specified Vale Price Type ...
     * @param  _toTime: datetime, Specified Time to Update Pivot and Zones ...
     *
     * @return ( bool )
     */
    bool GetPattern(
        XOHCL &_bar,
        XPivot &_pivot,
        XBoxZone &_zone,
        ENUM_X_BAR_PATTERN _pattern,
        ENUM_X_DIRECTION _patternDir = X_DIRECTION_NONE,
        ENUM_X_PRICE _peakPriceType = X_PRICE_HIGH,
        ENUM_X_PRICE _valePriceType = X_PRICE_LOW,
        datetime _toTime = NULL //
    )
    {
        //
        bool result = false;

        //
        // Prepare ...
        _bar.Clean();
        _zone.Clean();
        _pivot.Clean();

        //
        // Normalize ...
        if (!IsXValid(_peakPriceType))
        {
            _peakPriceType = X_PRICE_HIGH;
        }
        if (!IsXValid(_valePriceType))
        {
            _valePriceType = X_PRICE_LOW;
        }
        _toTime = NormalizeTime(_toTime);

        //
        // Validate ...
        result = IsValid() &&
                 HasChild(patterns) &&
                 IsXValid(_peakPriceType) &&
                 IsXValid(_valePriceType) &&
                 HasPattern(_pattern, _patternDir);
        if (!result)
        {
            return result;
        }

        //
        // Find Pattern Index ...
        int index = FindIndex(_pattern, patterns);
        result = IsValidIndex(index);
        if (!result)
        {
            return result;
        }

        //
        // Fill Common Patterns Data ...
        _bar = bar;
        result = ToPivot(
            _bar,
            _pivot,
            _patternDir,
            _pattern,
            _peakPriceType,
            _valePriceType //
        );
        result = result &&
                 ToBox(
                     _bar,
                     _zone,
                     _patternDir,
                     _pattern,
                     _peakPriceType,
                     _valePriceType //
                 );
        if (result)
        {
            _zone.to = _toTime;
        }

        //
        // Use Custom Data if Pattern Staisfied ...
        if (result)
        {
            //
            // OB ...
            if (_pattern == X_BAR_PATTERN_OB)
            {
                //
                _bar.Clean();
                _zone.Clean();
                _pivot.Clean();

                //
                // Result Preparation ...
                result = ob.IsValid() &&
                         ob.dir == _patternDir;
                if (result)
                {
                    //
                    _zone = ob;
                    result = _zone.FromBar(_bar);
                    if (result)
                    {
                        //
                        result = ToPivot(
                            _bar,
                            _pivot,
                            _patternDir,
                            _pattern,
                            _peakPriceType,
                            _valePriceType //
                        );
                    }
                }
            }
            //
            // FVG ...
            else if (_pattern == X_BAR_PATTERN_FVG)
            {
                //
                _bar.Clean();
                _zone.Clean();
                _pivot.Clean();

                //
                // Result Preparation ...
                result = fvg.IsValid() &&
                         fvg.dir == _patternDir;
                if (result)
                {
                    //
                    _zone = fvg;
                    result = _zone.FromBar(_bar);
                    if (result)
                    {
                        //
                        result = ToPivot(
                            _bar,
                            _pivot,
                            _patternDir,
                            _pattern,
                            _peakPriceType,
                            _valePriceType //
                        );
                    }
                }
            }
            //
            // Support ...
            else if (_pattern == X_BAR_PATTERN_SUPPORT)
            {
                //
                _bar.Clean();
                _zone.Clean();
                _pivot.Clean();

                //
                // Result Preparation ...
                result = support.IsValid();
                if (result)
                {
                    //
                    _zone = support;
                    result = _zone.AtBar(_bar);
                    if (result)
                    {
                        //
                        result = ToPivot(
                            _bar,
                            _pivot,
                            _patternDir,
                            _pattern,
                            _peakPriceType,
                            _valePriceType //
                        );
                    }
                }
            }
            //
            // Resistance ...
            else if (_pattern == X_BAR_PATTERN_RESISTANCE)
            {
                //
                _bar.Clean();
                _zone.Clean();
                _pivot.Clean();

                //
                // Result Preparation ...
                result = resistance.IsValid();
                if (result)
                {
                    //
                    _zone = resistance;
                    result = _zone.AtBar(_bar);
                    if (result)
                    {
                        //
                        result = ToPivot(
                            _bar,
                            _pivot,
                            _patternDir,
                            _pattern,
                            _peakPriceType,
                            _valePriceType //
                        );
                    }
                }
            }
        }

        //
        // Cleanup Resources ...
        if (!result)
        {
            //
            _bar.Clean();
            _zone.Clean();
            _pivot.Clean();
        }

        //
        return result;
    }

    /**
     * Extract All Exists Patterns Pivots ...
     *
     * @param  dest: XPivot, collection reference to holds Extracted Patterns ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction of Extractions ...
     *
     * @return ( int )
     */
    int GetPatterns(
        XPivot &dest[],
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);

        //
        // Validate ...
        bool has = IsValid() &&
                   HasChild(patterns);
        if (!has)
        {
            return result;
        }

        //
        // Do ...
        XOHCL iBar;
        XPivot iPivot;
        XBoxZone iZone;
        int count = ArraySize(patterns);
        for (int i = 0; i < count; i++)
        {
            //
            has = GetPattern(iBar, iPivot, iZone, patterns[i], forDir);
            if (has)
            {
                AddIfNotExists(iPivot, dest);
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iZone.Clean();
        iPivot.Clean();

        //
        return result;
    }

    /**
     * Extract All Exists Patterns Pivots ...
     *
     * @param  dest: XBoxZone, collection reference to holds Extracted Patterns ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction of Extractions ...
     *
     * @return ( int )
     */
    int GetPatterns(
        XBoxZone &dest[],
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);

        //
        // Validate ...
        bool has = IsValid() &&
                   HasChild(patterns);
        if (!has)
        {
            return result;
        }

        //
        // Do ...
        XOHCL iBar;
        XPivot iPivot;
        XBoxZone iZone;
        int count = ArraySize(patterns);
        for (int i = 0; i < count; i++)
        {
            //
            has = GetPattern(iBar, iPivot, iZone, patterns[i], forDir);
            if (has)
            {
                AddIfNotExists(iZone, dest);
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iZone.Clean();
        iPivot.Clean();

        //
        return result;
    }

    /**
     * Extract All Exists Patterns Pivots ...
     *
     * @param  dest: XPivot, collection reference to holds Extracted Patterns ...
     * @param  zones: XBoxZone, collection reference to holds Extracted Patterns ...
     * @param  forDir: ENUM_X_DIRECTION, Specified Direction of Extractions ...
     *
     * @return ( int )
     */
    int GetPatterns(
        XPivot &dest[],
        XBoxZone &zones[],
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int result = 0;

        //
        // Prepare ...
        XClean(dest);
        XClean(zones);

        //
        // Validate ...
        bool has = IsValid() &&
                   HasChild(patterns);
        if (!has)
        {
            return result;
        }

        //
        // Do ...
        XOHCL iBar;
        XPivot iPivot;
        XBoxZone iZone;
        int count = ArraySize(patterns);
        for (int i = 0; i < count; i++)
        {
            //
            has = GetPattern(iBar, iPivot, iZone, patterns[i], forDir);
            if (has)
            {
                //
                AddIfNotExists(iPivot, dest);
                AddIfNotExists(iZone, zones);
            }
        }

        //
        result = ArraySize(dest);

        //
        // Cleanup ...
        iBar.Clean();
        iZone.Clean();
        iPivot.Clean();

        //
        return result;
    }

    //
};

struct XPatternAnalysisConfig
{
    //
    // Props ...

    //
    // PV ...
    int pvValidationLength;
    ENUM_X_PRICE peakPriceType;
    ENUM_X_PRICE valePriceType;

    //
    // Momentum ...
    int momentumBarValidationLength;
    double momentumBarApprovedMultiplier;

    //
    // TrueGap ...
    double trueGapApprovedStrength;

    //
    // Pin ...
    double pinBarShadowApprovedMultiplier;
    double pinBarRangeMultiplier;
    bool pinBarForceDirection;

    //
    // Flag ...
    int flagPatternPullbackLength;

    //
    // FVG ...
    bool fvgForceType;

    //
    // OB ...
    bool obForceTwoBar;

    //
    // Support and Resistance ...
    int supportAndResistanceValidationLength;
    int supportAndResistanceLoopbackLength;

    //
    ENUM_X_DIRECTION allowedDirs[];
    ENUM_X_BAR_PATTERN allowedPatterns[];

    //
    // Constructor ...
    XPatternAnalysisConfig()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        // PV ...
        pvValidationLength = 0;
        peakPriceType = X_PRICE_NONE;
        valePriceType = X_PRICE_NONE;

        //
        // Momentum ...
        momentumBarValidationLength = 0;
        momentumBarApprovedMultiplier = 0;

        //
        // TrueGap ...
        trueGapApprovedStrength = 0;

        //
        // Pin ...
        pinBarShadowApprovedMultiplier = 0;
        pinBarRangeMultiplier = 0;
        pinBarForceDirection = false;

        //
        // Flag ...
        flagPatternPullbackLength = 0;

        //
        // FVG ...
        fvgForceType = false;

        //
        // OB ...
        obForceTwoBar = false;

        //
        // Support and Resistance ...
        supportAndResistanceValidationLength = 0;
        supportAndResistanceLoopbackLength = 0;

        //
        XClean(allowedDirs);
        XClean(allowedPatterns);

        //
        ZeroMemory(this);
    }

    /**
     * Default Configurations ...
     */
    void Default()
    {
        //
        // PV ...
        pvValidationLength = 5;
        peakPriceType = X_PRICE_HIGH;
        valePriceType = X_PRICE_LOW;

        //
        // Momentum ...
        momentumBarValidationLength = 3;
        momentumBarApprovedMultiplier = 3;

        //
        // TrueGap ...
        trueGapApprovedStrength = 1;

        //
        // Pin ...
        pinBarShadowApprovedMultiplier = 3;
        pinBarRangeMultiplier = 4;
        pinBarForceDirection = false;

        //
        // Flag ...
        flagPatternPullbackLength = 3;

        //
        // FVG ...
        fvgForceType = false;

        //
        // OB ...
        obForceTwoBar = false;

        //
        // Support and Resistance ...
        supportAndResistanceValidationLength = 21;
        supportAndResistanceLoopbackLength = 50;
    }

    //
    // Permissions ...

    /**
     * Check Specified Patterns Detection is Allowed or not ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern to Detect ...
     * @param  patternDir: ENUM_X_DIRECTION, which Pattern Direction to Detect ...
     *
     * @return ( bool )
     */
    bool CanDetect(
        ENUM_X_BAR_PATTERN pattern,
        ENUM_X_DIRECTION patternDir = X_DIRECTION_NONE //
    )
    {
        //
        bool result = false;

        //
        // Validate Pattern and
        // Check Pattern Exists ...
        result = IsXValid(pattern) &&
                 HasChild(allowedPatterns) &&
                 Contains(pattern, allowedPatterns);
        if (!result)
        {
            return result;
        }

        //
        // Now Check Direction ...
        int index = FindIndex(pattern, allowedPatterns);
        result = IsValidIndex(index) &&
                 HasChild(allowedDirs) &&
                 (!HasDirection(patternDir)
                      ? !HasDirection(allowedDirs[index])
                      : (!HasDirection(allowedDirs[index]) ||
                         patternDir == allowedDirs[index]));

        //
        return result;
    }

    //
    // Collection Manipulations ...

    /**
     * Remove all Allowed Patterns and Directions ...
     */
    void CleanPatterns()
    {
        //
        XClean(allowedDirs);
        XClean(allowedPatterns);
    }

    /**
     * Full Detect Patterns ...
     *
     * @param  forDir: ENUM_X_DIRECTION, which Pattern Direction to Detect ...
     */
    void FullPattern(
        ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
    )
    {
        //
        int count = GetAllBarPatters(allowedPatterns);
        for (int i = 0; i < count; i++)
        {
            //
            Add(
                forDir,
                allowedDirs //
            );
        }
    }

    /**
     * Add Specified Pattern to Detecte ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern to Detect ...
     * @param  patternDir: ENUM_X_DIRECTION, which Pattern Direction to Detect ...
     */
    void AddPattern(
        ENUM_X_BAR_PATTERN pattern,
        ENUM_X_DIRECTION patternDir = X_DIRECTION_NONE //
    )
    {
        //
        bool has = IsXValid(pattern);
        if (!has)
        {
            return;
        }

        //
        Add(
            pattern,
            allowedPatterns //
        );

        //
        Add(
            patternDir,
            allowedDirs //
        );
    }

    //
};

//
// Anaysis Draw Config Models ...

struct XPivotAnalysisDrawConfig
{
    //
    // Props ...

    //
    // Drawing Flags ...
    bool drawSupport;
    bool drawResistance;

    //
    bool drawPeakTrend;
    bool drawValeTrend;
    bool rayLeftTrends;
    bool rayRightTrends;

    //
    bool drawPivots;

    //
    // Drawing Configs ...
    XPOIDrawConfig drawConfig;

    //
    // Constructor ...
    XPivotAnalysisDrawConfig()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * This function fulfills the will of the developer
     */
    void Clean()
    {
        //
        // Drawing Flags ...

        //
        drawPivots = false;
        drawSupport = false;
        drawPeakTrend = false;
        drawValeTrend = false;
        rayLeftTrends = false;
        rayRightTrends = false;
        drawResistance = false;

        //
        // Drawing Configs ...
        drawConfig.Clean();

        //
        ZeroMemory(this);
    }

    /**
     * Default Configurations ...
     */
    void Default()
    {
        //
        // Drawing Flags ...

        //
        drawPivots = false;
        drawSupport = true;
        drawPeakTrend = false;
        drawValeTrend = false;
        rayLeftTrends = true;
        rayRightTrends = true;
        drawResistance = true;

        //
        // Drawing Configs ...
        drawConfig.Default();
    }

    /**
     * No Draw Abilioty ...
     */
    void Off()
    {
        //
        drawPivots = false;
        drawSupport = false;
        drawPeakTrend = false;
        drawValeTrend = false;
        drawResistance = false;
    }

    /**
     * Full Draw Abilioty ...
     */
    void Full()
    {
        //
        drawPivots = true;
        drawSupport = true;
        drawPeakTrend = true;
        drawValeTrend = true;
        drawResistance = true;
    }

    //
};

struct XPatternAnalysisDrawConfig
{
    //
    // Props ...

    //
    // Patterns ...

    //
    ENUM_X_BAR_PATTERN allowedDraws[];      // Allowed Draw Patterns
    ENUM_X_BAR_PATTERN allowedDrawZones[];  // Allowed Draw Pattern Zones
    ENUM_X_BAR_PATTERN allowedDrawPivots[]; // Allowed Draw Pattern Pivots

    //
    XPOIDrawConfig drawConfig;

    //
    // Constructor ...
    XPatternAnalysisDrawConfig()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        // Drawing Configs ...
        drawConfig.Clean();

        //
        XClean(allowedDraws);
        XClean(allowedDrawZones);
        XClean(allowedDrawPivots);
    }

    /**
     * Default Drawing Config ...
     */
    void Default()
    {
        //
        // Drawing Configs ...
        drawConfig.Default();

        //
        Full();

        //
        // Remove Unused From Zones ...
        Remove(X_BAR_PATTERN_PINNED, allowedDrawZones);
        Remove(X_BAR_PATTERN_MOMENTUM, allowedDrawZones);
        Remove(X_BAR_PATTERN_ENGULFED, allowedDrawZones);
        Remove(X_BAR_PATTERN_TRUE_GAPED, allowedDrawZones);

        //
        // Remove Unused From Pivots ...
        Remove(X_BAR_PATTERN_OB, allowedDrawPivots);
        Remove(X_BAR_PATTERN_FVG, allowedDrawPivots);
        Remove(X_BAR_PATTERN_SUPPORT, allowedDrawPivots);
        Remove(X_BAR_PATTERN_RESISTANCE, allowedDrawPivots);
    }

    /**
     * No Draw Abilioty ...
     */
    void Off()
    {
        //
        XClean(allowedDraws);
        XClean(allowedDrawZones);
        XClean(allowedDrawPivots);
    }

    /**
     * Full Draw Abilioty ...
     */
    void Full()
    {
        //
        GetAllBarPatters(allowedDraws);
        GetAllBarPatters(allowedDrawZones);
        GetAllBarPatters(allowedDrawPivots);
    }

    //
    // Checkers ...

    /**
     * Check Can Draw Specified Pattern ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     *
     * @return ( bool )
     */
    bool CanDraw(ENUM_X_BAR_PATTERN pattern)
    {
        //
        bool result = false;

        //
        result = HasChild(allowedDraws) &&
                 Contains(pattern, allowedDraws);

        //
        return result;
    }

    /**
     * Check Can Draw Specified Pattern's Zone ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     *
     * @return ( bool )
     */
    bool CanDrawZone(ENUM_X_BAR_PATTERN pattern)
    {
        //
        bool result = false;

        //
        result =
            CanDraw(pattern) &&
            HasChild(allowedDrawZones) &&
            Contains(pattern, allowedDrawZones);

        //
        return result;
    }

    /**
     * Check Can Draw Specified Pattern's Pivot ...
     *
     * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern ...
     *
     * @return ( bool )
     */
    bool CanDrawPivot(ENUM_X_BAR_PATTERN pattern)
    {
        //
        bool result = false;

        //
        result =
            CanDraw(pattern) &&
            HasChild(allowedDrawPivots) &&
            Contains(pattern, allowedDrawPivots);

        //
        return result;
    }
};

//
// Extensions ...

bool FindByTime(
    int &index,
    datetime time,
    XPatternAnalysis &source[] //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    index = -1;

    //
    // Validate ...
    result = IsXValid(time) &&
             HasChild(source);
    if (!result)
    {
        return result;
    }

    //
    // Do ...
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        result = source[i].bar.time == time;
        if (result)
        {
            //
            index = i;
            break;
        }
    }

    //
    result = IsValidIndex(index);

    //
    return result;
}

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-poi.extensions.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Seriallize Library
// ---------------------------------------
// Name: XPOILib
// Description: All models related to POIs ...
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright `023, SaherElm IT Center"
#property link "https://www.saherelm.ir"
#property version "1.00"
#property strict

//
// Imports ...
#include "../Libraries/x-saherelm.x-ohcl.lib.mq5"
#include "../Libraries/x-saherelm.x-poi.lib.mq5"

//
// Extensions ...
//+------------------------------------------------------------------+
//| Oldest Detection                                                 |
//+------------------------------------------------------------------+
/**
 * Get Oldest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetOldest(
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.from > source[i].from;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Oldest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( int )
 */
int GetOldestByDir(
    XBoxZone &source[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            (!HasDirection(forDir)
                 ? true
                 : forDir == source[i].dir) &&
            (!selected.IsValid()
                 ? true
                 : selected.from > source[i].from);
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Oldest Index of Specified Collection ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetOldestByBar(
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    if (!bar.IsValid())
    {
        return result;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool isBoxValidForBar = IsBoxPlaceValidForBar(bar, source[i]);
        if (!isBoxValidForBar)
        {
            continue;
        }

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.from > source[i].from;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Oldest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  type: ENUM_X_PIVOT_TYPE, Specified Points Type ...
 *
 * @return ( int )
 */
int GetOldest(
    XPivot &source[],
    ENUM_X_PIVOT_TYPE type = X_PIVOT_TYPE_NONE //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(source))
    {
        return result;
    }

    //
    bool has = false;
    XPivot iPoint;
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        // Passed Type ...
        has = !IsXValid(type)
                  ? true
                  : type == source[i].type;

        //
        // Check Can Assign ...
        has =
            has &&
            (!iPoint.IsValid() ||
             (iPoint.IsValid() &&
              source[i].GetAge() > iPoint.GetAge()));
        if (has)
        {
            //
            result = i;
            iPoint = source[i];
        }
    }

    //
    // Validate Result ...
    has =
        iPoint.IsValid() &&
        IsValidIndex(result);
    if (!has)
    {
        //
        result = -1;
        iPoint.Clean();
        return result;
    }

    //
    iPoint.Clean();

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Youngest Detection                                               |
//+------------------------------------------------------------------+
/**
 * Get Youngest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetYoungest(
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.from < source[i].from;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Youngest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( int )
 */
int GetYoungestByDir(
    XBoxZone &source[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            (!HasDirection(forDir)
                 ? true
                 : forDir == source[i].dir) &&
            (!selected.IsValid()
                 ? true
                 : selected.from < source[i].from);
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Youngest Index of Specified Collection ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetYoungestByBar(
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    if (!bar.IsValid())
    {
        return result;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool isBoxValidForBar = IsBoxPlaceValidForBar(bar, source[i]);
        if (!isBoxValidForBar)
        {
            continue;
        }

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.from < source[i].from;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Youngest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  type: ENUM_X_PIVOT_TYPE, Specified Points Type ...
 *
 * @return ( int )
 */
int GetYoungest(
    XPivot &source[],
    ENUM_X_PIVOT_TYPE type = X_PIVOT_TYPE_NONE //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(source))
    {
        return result;
    }

    //
    bool has = false;
    XPivot iPoint;
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        // Passed Type ...
        has = !IsXValid(type)
                  ? true
                  : type == source[i].type;

        //
        // Check Can Assign ...
        has =
            has &&
            (!iPoint.IsValid() ||
             (iPoint.IsValid() &&
              source[i].GetAge() < iPoint.GetAge()));
        if (has)
        {
            //
            result = i;
            iPoint = source[i];
        }
    }

    //
    // Validate Result ...
    has =
        iPoint.IsValid() &&
        IsValidIndex(result);
    if (!has)
    {
        //
        result = -1;
        iPoint.Clean();
        return result;
    }

    //
    iPoint.Clean();

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Highest Detection                                                |
//+------------------------------------------------------------------+
/**
 * Get Highest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetHighest(
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.upper < source[i].upper;
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Highest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( int )
 */
int GetHighestByDir(
    XBoxZone &source[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            (!HasDirection(forDir)
                 ? true
                 : forDir == source[i].dir) &&
            (!selected.IsValid()
                 ? true
                 : selected.upper < source[i].upper);
        if (canSet)
        {
            //
            result = i;
            selected = source[i];
        }
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Highest Index of Specified Collection ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetHighestByBar(
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    if (!bar.IsValid())
    {
        return result;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        XBoxZone iBox = source[i];

        //
        bool isBoxValidForBar = IsBoxPlaceValidForBar(bar, iBox);
        if (!isBoxValidForBar)
        {
            //
            iBox.Clean();

            //
            continue;
        }

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.upper < iBox.upper;
        if (canSet)
        {
            //
            result = i;
            selected = iBox;
        }

        //
        iBox.Clean();
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Highest Bar Price Index of Specified Collection ...
 *
 * @param  source: reference collection, Provides Data Source ...
 *
 * @return ( int )
 */
int GetHighestBarPrice(
    XOHCL &source[],
    ENUM_X_PRICE priceType //
)
{
    //
    int result = -1;

    //
    // Validate ...
    bool has = HasChild(source) &&
               IsXValid(priceType);
    if (!has)
    {
        return result;
    }

    //
    double price = EMPTY_VALUE;
    double selected = EMPTY_VALUE;
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        price = source[i].GetPrice(priceType);
        has = !NotEmptyZero(selected)
                  ? true
                  : price > selected;
        if (has)
        {
            //
            result = i;
            selected = price;
        }
    }

    //
    return result;
}

/**
 * Get Highest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  type: ENUM_X_PIVOT_TYPE, Specified Points Type ...
 *
 * @return ( int )
 */
int GetHighest(
    XPivot &source[],
    ENUM_X_PIVOT_TYPE type = X_PIVOT_TYPE_NONE //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(source))
    {
        return result;
    }

    //
    bool has = false;
    XPivot iPoint;
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        // Passed Type ...
        has = !IsXValid(type)
                  ? true
                  : type == source[i].type;

        //
        // Check Can Assign ...
        has =
            has &&
            (!iPoint.IsValid() ||
             (iPoint.IsValid() &&
              source[i].value > iPoint.value));
        if (has)
        {
            //
            result = i;
            iPoint = source[i];
        }
    }

    //
    // Validate Result ...
    has =
        iPoint.IsValid() &&
        IsValidIndex(result);
    if (!has)
    {
        //
        result = -1;
        iPoint.Clean();
        return result;
    }

    //
    iPoint.Clean();

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Loest Detection                                                  |
//+------------------------------------------------------------------+
/**
 * Get Lowest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetLowest(
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        XBoxZone iBox = source[i];

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.lower > iBox.lower;
        if (canSet)
        {
            //
            result = i;
            selected = iBox;
        }

        //
        iBox.Clean();
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Lowest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( int )
 */
int GetLowestByDir(
    XBoxZone &source[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = -1;

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        XBoxZone iBox = source[i];

        //
        bool canSet =
            (!HasDirection(forDir)
                 ? true
                 : forDir == source[i].dir) &&
            (!selected.IsValid()
                 ? true
                 : selected.lower > iBox.lower);
        if (canSet)
        {
            //
            result = i;
            selected = iBox;
        }

        //
        iBox.Clean();
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Lowest Index of Specified Collection ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  source: Specified Collection ...
 *
 * @return ( int )
 */
int GetLowestByBar(
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    if (!bar.IsValid())
    {
        return result;
    }

    //
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XBoxZone selected;
    for (int i = 0; i < count; i++)
    {
        //
        XBoxZone iBox = source[i];

        //
        bool isBoxValidForBar = IsBoxPlaceValidForBar(bar, iBox);
        if (!isBoxValidForBar)
        {
            //
            iBox.Clean();

            //
            continue;
        }

        //
        bool canSet =
            !selected.IsValid()
                ? true
                : selected.lower > iBox.lower;
        if (canSet)
        {
            //
            result = i;
            selected = iBox;
        }

        //
        iBox.Clean();
    }

    //
    selected.Clean();

    //
    return result;
}

/**
 * Get Lowest Bar Price Index of Specified Collection ...
 *
 * @param  source: reference collection, Provides Data Source ...
 *
 * @return ( int )
 */
int GetLowestBarPrice(
    XOHCL &source[],
    ENUM_X_PRICE priceType //
)
{
    //
    int result = -1;

    //
    // Validate ...
    bool has = HasChild(source) &&
               IsXValid(priceType);
    if (!has)
    {
        return result;
    }

    //
    double price = EMPTY_VALUE;
    double selected = EMPTY_VALUE;
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        price = source[i].GetPrice(priceType);
        has = !NotEmptyZero(selected)
                  ? true
                  : price < selected;
        if (has)
        {
            //
            result = i;
            selected = price;
        }
    }

    //
    return result;
}

/**
 * Get Lowest Index of Specified Collection ...
 *
 * @param  source: Specified Collection ...
 * @param  type: ENUM_X_PIVOT_TYPE, Specified Points Type ...
 *
 * @return ( int )
 */
int GetLowest(
    XPivot &items[],
    ENUM_X_PIVOT_TYPE type = X_PIVOT_TYPE_NONE //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(items))
    {
        return result;
    }

    //
    bool has = false;
    XPivot iPoint;
    int count = ArraySize(items);
    for (int i = 0; i < count; i++)
    {
        //
        // Passed Type ...
        has = !IsXValid(type)
                  ? true
                  : type == items[i].type;

        //
        // Check Can Assign ...
        has =
            has &&
            (!iPoint.IsValid() ||
             (iPoint.IsValid() &&
              items[i].value < iPoint.value));
        if (has)
        {
            //
            result = i;
            iPoint = items[i];
        }
    }

    //
    // Validate Result ...
    has =
        iPoint.IsValid() &&
        IsValidIndex(result);
    if (!has)
    {
        //
        result = -1;
        iPoint.Clean();
        return result;
    }

    //
    iPoint.Clean();

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Box                                                              |
//+------------------------------------------------------------------+
/**
 * Extract all items which appears Before Specified Time ...
 *
 * @param  _time: Specified Time ...
 * @param  dest: refrence collection to hold result ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetBeforeItems(
    datetime _time,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    int count = ArraySize(source);
    bool has = HasChild(source) &&
               IsSpecifiedValid(_time);
    if (!has)
    {
        return result;
    }

    //
    // Loop through Items ...
    for (int i = 0; i < count; i++)
    {
        //
        has = source[i].from < _time;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all items which appears After Specified Time ...
 *
 * @param  _time: Specified Time ...
 * @param  dest: refrence collection to hold result ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetAfterItems(
    datetime _time,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    int count = ArraySize(source);
    bool has = HasChild(source) &&
               IsSpecifiedValid(_time);
    if (!has)
    {
        return result;
    }

    //
    // Loop through Items ...
    for (int i = 0; i < count; i++)
    {
        //
        has = source[i].from > _time;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all items which appears Above Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  dest: refrence collection to hold result ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetAboveItems(
    double _value,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    int count = ArraySize(source);
    bool has = HasChild(source) &&
               NotEmptyZero(_value);
    if (!has)
    {
        return result;
    }

    //
    // Loop through Items ...
    for (int i = 0; i < count; i++)
    {
        //
        has = source[i].lower > _value;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all items which appears Below Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  dest: refrence collection to hold result ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetBelowItems(
    double _value,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    int count = ArraySize(source);
    bool has = HasChild(source) &&
               NotEmptyZero(_value);
    if (!has)
    {
        return result;
    }

    //
    // Loop through Items ...
    for (int i = 0; i < count; i++)
    {
        //
        has = source[i].upper < _value;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Get Oldest Item Index of all items which appears Before Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetOldestBefore(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBeforeItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetOldest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Oldest Item Index of all items which appears After Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetOldestAfter(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAfterItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetOldest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Oldest Item Index of all items which appears Above Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetOldestAbove(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAboveItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetOldest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Oldest Item Index of all items which appears Below Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetOldestBelow(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBelowItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetOldest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Youngest Item Index of all items which appears Before Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetYoungestBefore(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBeforeItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetYoungest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Youngest Item Index of all items which appears After Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetYoungestAfter(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAfterItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetYoungest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Youngest Item Index of all items which appears Above Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetYoungestAbove(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAboveItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetYoungest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Youngest Item Index of all items which appears Below Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetYoungestBelow(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBelowItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetYoungest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Highest Item Index of all items which appears Before Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetHighestBefore(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBeforeItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetHighest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Highest Item Index of all items which appears After Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetHighestAfter(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAfterItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetHighest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Highest Item Index of all items which appears Above Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetHighestAbove(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAboveItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetHighest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Highest Item Index of all items which appears Below Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetHighestBelow(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBelowItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetHighest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Lowest Item Index of all items which appears Before Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetLowestBefore(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBeforeItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetLowest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Lowest Item Index of all items which appears After Specified Value ...
 *
 * @param  _time: Specified Time ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetLowestAfter(
    datetime _time,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAfterItems(
        _time,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetLowest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Lowest Item Index of all items which appears Above Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetLowestAbove(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetAboveItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetLowest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Get Lowest Item Index of all items which appears Below Specified Value ...
 *
 * @param  _value: Specified Value ...
 * @param  source: refrence collection to provides data source ...
 *
 * @return ( int )
 */
int GetLowestBelow(
    double _value,
    XBoxZone &source[] //
)
{
    //
    int result = -1;

    //
    XBoxZone tmps[];
    int tmpsCount = GetBelowItems(
        _value,
        tmps,  // Dest ...
        source // Source ...
    );
    bool has = IsValidSize(tmpsCount);
    int idx = GetLowest(tmps);
    has =
        has &&
        IsValidIndex(idx) &&
        FindIndex(
            result,
            tmps[idx],
            source //
        );
    SpecifiedClean(tmps);

    //
    return result;
}

/**
 * Validate an Item Strength ...
 *
 * @param  item: Specified Item for Validating ...
 * @param  minAllowedStrength: int, Minimum required Strength in Point ...
 *
 * @return ( bool )
 */
bool ValidateStrength(
    XBoxZone &item,
    int minAllowedStrength = 0 //
)
{
    //
    bool result = false;

    //
    // Normalize ...
    minAllowedStrength = NormalizeInt(minAllowedStrength, 0);

    //
    // Validate ...
    result = item.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // If doent provide Specified Strength Validate Zone ...
    result = minAllowedStrength <= 0;
    if (result)
    {
        return result;
    }

    //
    double points = GetPoints(item.symbol);
    double requiredStrength = minAllowedStrength * points;
    double itemStrength = item.upper - item.lower;

    //
    result =
        itemStrength > 0 &&
        requiredStrength > 0 &&
        itemStrength > requiredStrength;

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Sort                                                             |
//+------------------------------------------------------------------+

/**
 * Allowed Sort Param ...
 **/
enum ENUM_X_SORT_BY
{
    X_SORT_BY_NONE = 0,  // None
    X_SORT_BY_DATE = 1,  // by Date
    X_SORT_BY_VALUE = 2, // by Date
};

/**
 * Check Specified Sort By Validation ...
 *
 * @param  value: ENUM_X_SORT_BY, value to Validate ...
 *
 * @return ( bool )
 */
bool IsXValid(ENUM_X_SORT_BY value)
{
    return value != X_SORT_BY_NONE;
}

/**
 * Select Specified Index of a Collection by Sort Params ...
 *
 * @param  source: Collection for Search ...
 * @param  sortBy: ENUM_X_SORT_BY, Specified Sort Param ...
 * @param  sortDir: ENUM_X_DIRECTION, Specified Sort Direction ...
 *
 * @return ( int )
 */
template <typename T>
int SelectItemForSorting(
    T &source[],
    ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
    ENUM_X_DIRECTION sortDir = X_DIRECTION_BULLISH //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(source) ||
        !IsXValid(sortBy) ||
        !HasDirection(sortDir))
    {
        return result;
    }

    //
    // Sort By Date ...
    if (sortBy == X_SORT_BY_DATE)
    {
        //
        // Select Based on Sorting Direction ...

        //
        // form Oldest to Youngest ...
        if (sortDir == X_DIRECTION_BULLISH)
        {
            result = GetOldest(source);
        }
        //
        // form Youngest to Oldest ...
        else if (sortDir == X_DIRECTION_BEARISH)
        {
            result = GetYoungest(source);
        }
        //
        // Not Valid ...
        else
        {
            result = -1;
        }
    }
    //
    // Sort By Value ...
    else if (sortBy == X_SORT_BY_VALUE)
    {
        //
        // form Lowst to Highest ...
        if (sortDir == X_DIRECTION_BULLISH)
        {
            result = GetLowest(source);
        }
        //
        // form Highest to Lowest ...
        else if (sortDir == X_DIRECTION_BEARISH)
        {
            result = GetHighest(source);
        }
        //
        // Not Valid ...
        else
        {
            result = -1;
        }
    }
    //
    // Not Valid ...
    else
    {
        result = -1;
    }

    //
    return result;
}

/**
 * Select Specified Index of a Collection by Sort Params ...
 *
 * @param  source: Collection for Search ...
 * @param  sortBy: ENUM_X_SORT_BY, Specified Sort Param ...
 * @param  sortDir: ENUM_X_DIRECTION, Specified Sort Direction ...
 *
 * @return ( int )
 */
int SelectBarForSorting(
    XOHCL &source[],
    ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
    ENUM_X_DIRECTION sortDir = X_DIRECTION_BULLISH,
    ENUM_X_PRICE priceType = X_PRICE_NONE //
)
{
    //
    int result = -1;

    //
    // Validate ...
    if (!HasChild(source) ||
        !IsXValid(sortBy) ||
        !HasDirection(sortDir))
    {
        return result;
    }

    //
    // Sort By Date ...
    if (sortBy == X_SORT_BY_DATE)
    {
        //
        // Select Based on Sorting Direction ...

        //
        // form Oldest to Youngest ...
        if (sortDir == X_DIRECTION_BULLISH)
        {
            result = GetOldest(source);
        }
        //
        // form Youngest to Oldest ...
        else if (sortDir == X_DIRECTION_BEARISH)
        {
            result = GetYoungest(source);
        }
        //
        // Not Valid ...
        else
        {
            result = -1;
        }
    }
    //
    // Sort By Value ...
    else if (sortBy == X_SORT_BY_VALUE)
    {
        //
        // form Lowst to Highest ...
        if (sortDir == X_DIRECTION_BULLISH)
        {
            result = GetLowest(source, priceType);
        }
        //
        // form Highest to Lowest ...
        else if (sortDir == X_DIRECTION_BEARISH)
        {
            result = GetHighest(source, priceType);
        }
        //
        // Not Valid ...
        else
        {
            result = -1;
        }
    }
    //
    // Not Valid ...
    else
    {
        result = -1;
    }

    //
    return result;
}

/**
 * Apply Sort on Specified Collection ...
 *
 * @param  source: Collection Reference to Sort ...
 * @param  sortBy: ENUM_X_SORT_BY, Specified Sort Param ...
 * @param  sortDir: ENUM_X_DIRECTION, Specified Sort Direction ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 */
template <typename T>
void Sort(
    T &source[],
    ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
    ENUM_X_DIRECTION sortDir = X_DIRECTION_BEARISH,
    int maxAllowed = 0 //
)
{
    //
    int idx = -1;
    bool has = false;

    //
    // Validate ...
    has =
        IsXValid(sortBy) &&
        HasChild(source) &&
        HasDirection(sortDir);
    if (!has)
    {
        return;
    }

    //
    // Copy source for Manipulating ...
    T tmps[];
    Copy(
        source,
        tmps //
    );

    //
    // Clean Source Collection for Adding Sorted Items ...
    Clean(source);

    //
    // Loop Through Temp Collection Has Child ...
    while (HasChild(tmps))
    {
        //
        // Select Based on Sorting Parameter ...
        idx = SelectItemForSorting(
            tmps,
            sortBy,
            sortDir //
        );

        //
        // Validate Detected Index ...
        has = IsValidIndex(idx);
        if (!has)
        {
            break;
        }

        //
        // Add Detected Indexed Item to Source ...
        AddRef(
            tmps[idx],
            source //
        );

        //
        // Remove Item From Temp Collection ...
        ArrayRemove(
            tmps,
            idx,
            1 //
        );
    }

    //
    // Cleanup Collection if Max Items Reached ...
    if (maxAllowed > 0)
    {
        //
        CleanupArray(
            source,
            maxAllowed //
        );
    }

    //
    // Cleanup Resources ...
    SpecifiedClean(tmps);
}

/**
 * Apply Sort on Specified Collection ...
 *
 * @param  source: Collection Reference to Sort ...
 * @param  sortBy: ENUM_X_SORT_BY, Specified Sort Param ...
 * @param  sortDir: ENUM_X_DIRECTION, Specified Sort Direction ...
 * @param  priceType: ENUM_X_PRICE, Price Selecting Method ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 */
void SortBar(
    XOHCL &source[],
    ENUM_X_SORT_BY sortBy = X_SORT_BY_DATE,
    ENUM_X_DIRECTION sortDir = X_DIRECTION_BULLISH,
    ENUM_X_PRICE priceType = X_PRICE_NONE,
    int maxAllowed = 0 //
)
{
    //
    int idx = -1;
    bool has = false;

    //
    // Validate ...
    has =
        IsXValid(sortBy) &&
        HasChild(source) &&
        HasDirection(sortDir);
    if (!has)
    {
        return;
    }

    //
    // Copy source for Manipulating ...
    XOHCL tmps[];
    Copy(
        source,
        tmps //
    );

    //
    // Clean Source Collection for Adding Sorted Items ...
    Clean(source);

    //
    // Loop Through Temp Collection Has Child ...
    while (HasChild(tmps))
    {
        //
        // Select Based on Sorting Parameter ...
        idx = SelectBarForSorting(
            tmps,
            sortBy,
            sortDir,
            priceType //
        );

        //
        // Validate Detected Index ...
        has = IsValidIndex(idx);
        if (!has)
        {
            break;
        }

        //
        // Add Detected Indexed Item to Source ...
        AddRef(
            tmps[idx],
            source //
        );

        //
        // Remove Item From Temp Collection ...
        ArrayRemove(
            tmps,
            idx,
            1 //
        );
    }

    //
    // Cleanup Collection if Max Items Reached ...
    if (maxAllowed > 0)
    {
        //
        CleanupArray(
            source,
            maxAllowed //
        );
    }

    //
    // Cleanup Resources ...
    SpecifiedClean(tmps);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Extraction                                                       |
//+------------------------------------------------------------------+

/**
 * Extract Specific Types of Pivots From a Collection ...
 *
 * @param  dest: reference Collection to Hold result ...
 * @param  source: Collection to Find Type Specific items ...
 * @param  _type: Specified item Type ...
 *
 * @return ( int )
 */
int ExtractPivots(
    XPivot &dest[],
    XPivot &source[],
    ENUM_X_PIVOT_TYPE _type //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    int count = ArraySize(source);
    bool has =
        IsXValid(_type) &&
        IsValidSize(count);
    if (!has)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        // Check Type Passing ...
        has = _type == source[i].type;
        if (!has)
        {
            continue;
        }

        //
        AddIfNotExists(
            source[i],
            dest //
        );
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract Same Time Pivots ...
 *
 * @param  item: XPivot, reference to Specified Pivot for Detect Same Times ...
 * @param  dest: XPivot, collection reference to holds Same Times Pivots ...
 * @param  source: XPivot, collection reference to Provide Data Source ...
 *
 * @return ( int )
 */
int ExtractSameTimePivots(
    XPivot &item,
    XPivot &dest[],
    XPivot &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    // Validate ...
    bool has = HasChild(source) &&
               item.IsValid();
    if (!has)
    {
        return result;
    }

    //
    // Do ...
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = item.time == source[i].time;
        if (has)
        {
            AddIfNotExists(source[i], dest);
        }
    }

    //
    // Add item as a Child ...
    AddIfNotExists(item, dest);

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract Pivots Data for Linear Regression Calculations ...
 *
 * @param  xData: refrence Collection to Holds X-Axis Data ...
 * @param  yData: refrence Collection to Holds Y-Axis Data ...
 * @param  source: pivots Collection for Extraction ...
 *
 * @return ( int )
 */
int ExtractPivotsData(
    double &xData[],
    double &yData[],
    XPivot &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(xData);
    SpecifiedClean(yData);

    //
    // Validate ...
    int count = ArraySize(source);
    if (!IsValidSize(count))
    {
        return result;
    }

    //
    XOHCL iBar;
    bool has = false;
    double iX = EMPTY_VALUE;
    double iY = EMPTY_VALUE;
    for (int i = 0; i < count; i++)
    {
        //
        // Get Pivot Bar ...
        has = source[i].GetBar(iBar);
        if (!has)
        {
            continue;
        }

        //
        iX = iBar.Index();
        iY = source[i].value;

        //
        Add(
            iX,
            xData //
        );

        //
        Add(
            iY,
            yData //
        );

        //
        iBar.Clean();
    }

    //
    iBar.Clean();

    //
    result = MathMin(ArraySize(xData), ArraySize(yData));

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Support and Resistance                                           |
//+------------------------------------------------------------------+
/**
 * Extract Support Levels of Pivots Collection ...
 *
 * @param  _minor: refrence to Holds Minor Level ...
 * @param  _major: refrence to Holds Major Level ...
 * @param  source: Collection of Pivots ...
 *
 * @return ( bool )
 */
bool ExtractSupport(
    double &_minor,
    double &_major,
    XPivot &source[] //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    _minor = EMPTY_VALUE;
    _major = EMPTY_VALUE;

    //
    // Validate ...
    int count = ArraySize(source);
    result = IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    bool has = false;
    double tmp = EMPTY_VALUE;
    double iValue = EMPTY_VALUE;
    for (int i = 0; i < count; i++)
    {
        //
        // Check Type ...
        has = source[i].IsVale();
        if (!has)
        {
            continue;
        }

        //
        // Reading Value ...
        iValue = source[i].value;

        //
        // Check Can Change Major ...
        has = !NotEmpty(_major) ||
              _major > iValue;
        if (has)
        {
            //
            tmp = _major;
            _major = iValue;
        }
        //
        // Check Can Change Minor ...
        else
        {
            //
            // Change Minor ...
            has = !NotEmpty(tmp) ||
                  tmp > iValue;
            if (has)
            {
                tmp = iValue;
            }
        }
    }

    //
    // Last Update of Minore Minor ...
    has = NotEmpty(tmp);
    if (has)
    {
        _minor = tmp;
    }

    //
    result = NotEmpty(_major) ||
             NotEmpty(_minor);

    //
    return result;
}

/**
 * Extract Resistance Levels of Pivots Collection ...
 *
 * @param  _minor: refrence to Holds Minor Level ...
 * @param  _major: refrence to Holds Major Level ...
 * @param  source: Collection of Pivots ...
 *
 * @return ( bool )
 */
bool ExtractResistances(
    double &_minor,
    double &_major,
    XPivot &source[] //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    _minor = EMPTY_VALUE;
    _major = EMPTY_VALUE;

    //
    // Validate ...
    int count = ArraySize(source);
    result = IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    bool has = false;
    double tmp = EMPTY_VALUE;
    double iValue = EMPTY_VALUE;
    for (int i = 0; i < count; i++)
    {
        //
        // Check Type ...
        has = source[i].IsPeak();
        if (!has)
        {
            continue;
        }

        //
        // Reading Value ...
        iValue = source[i].value;

        //
        // Check Can Change Major ...
        has = !NotEmpty(_major) ||
              _major < iValue;
        if (has)
        {
            //
            tmp = _major;
            _major = iValue;
        }
        //
        // Check Can Change Minor ...
        else
        {
            //
            // Change Minor ...
            has = !NotEmpty(tmp) ||
                  tmp < iValue;
            if (has)
            {
                tmp = iValue;
            }
        }
    }

    //
    // Last Update of Minore Minor ...
    has = NotEmpty(tmp);
    if (has)
    {
        _minor = tmp;
    }

    //
    result = NotEmpty(_major) ||
             NotEmpty(_minor);

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Find Index                                                       |
//+------------------------------------------------------------------+
/**
 * Find Specified Item index in a Collection ...
 *
 * @param  index: int, reference to Hold Index if Exists ...
 * @param  item: Specified item to Detect ...
 * @param  source: Collection to Search for item ...
 *
 * @return ( bool )
 */
bool FindIndex(
    int &index,
    XBoxZone &item,
    XBoxZone &source[] //
)
{
    //
    bool result = false;

    //
    index = -1;

    //
    int count = ArraySize(source);
    result = item.IsValid() &&
             IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        bool isSame = item.IsSameAs(source[i]);
        if (isSame)
        {
            //
            index = i;
            break;
        }
    }

    //
    result = IsValidIndex(index);

    //
    return result;
}

/**
 * Find Specified Item index in a Collection ...
 *
 * @param  index: int, reference to Hold Index if Exists ...
 * @param  item: Specified item to Detect ...
 * @param  source: Collection to Search for item ...
 *
 * @return ( bool )
 */
bool FindIndex(
    int &index,
    XPivot &item,
    XPivot &source[] //
)
{
    //
    bool result = false;

    //
    index = -1;

    //
    int count = ArraySize(source);
    result = item.IsValid() &&
             IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        bool isSame = item.IsSameAs(source[i]);
        if (isSame)
        {
            //
            index = i;
            break;
        }
    }

    //
    result = IsValidIndex(index);

    //
    return result;
}

/**
 * Find Specified Item index in a Collection ...
 *
 * @param  index: int, reference to Hold Index if Exists ...
 * @param  item: Specified item to Detect ...
 * @param  source: Collection to Search for item ...
 *
 * @return ( bool )
 */
bool FindIndex(
    int &index,
    XDirectionShift &item,
    XDirectionShift &source[] //
)
{
    //
    bool result = false;

    //
    index = -1;

    //
    int count = ArraySize(source);
    result = item.IsValid() &&
             IsValidSize(count);
    if (!result)
    {
        return result;
    }

    //
    for (int i = 0; i < count; i++)
    {
        //
        bool isSame = item.IsSameAs(source[i]);
        if (isSame)
        {
            //
            index = i;
            break;
        }
    }

    //
    result = IsValidIndex(index);

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Remove                                                           |
//+------------------------------------------------------------------+
/**
 * Remove Specified Item from a Collection ...
 *
 * @param  item: Specified Item ...
 * @param  source: Specified Collection for Remove item from it ...
 *
 * @return ( bool )
 */
bool Remove(
    XPivot &item,
    XPivot &source[] //
)
{
    //
    bool result = false;

    //
    result =
        item.IsValid() &&
        HasChild(source);
    if (!result)
    {
        return result;
    }

    //
    int idx = -1;
    result = FindIndex(
        idx,
        item,
        source //
    );
    if (!result)
    {
        return result;
    }

    //
    result = ArrayRemove(
        source,
        idx,
        1 //
    );

    //
    return result;
}

/**
 * Remove Specified Item from a Collection ...
 *
 * @param  item: Specified Item ...
 * @param  source: Specified Collection for Remove item from it ...
 *
 * @return ( bool )
 */
bool Remove(
    XBoxZone &item,
    XBoxZone &source[] //
)
{
    //
    bool result = false;

    //
    result =
        item.IsValid() &&
        HasChild(source);
    if (!result)
    {
        return result;
    }

    //
    int idx = -1;
    result = FindIndex(
        idx,
        item,
        source //
    );
    if (!result)
    {
        return result;
    }

    //
    result = ArrayRemove(
        source,
        idx,
        1 //
    );

    //
    return result;
}

/**
 * Remove Specified Item from a Collection ...
 *
 * @param  item: Specified Item ...
 * @param  source: Specified Collection for Remove item from it ...
 *
 * @return ( bool )
 */
bool Remove(
    XDirectionShift &item,
    XDirectionShift &source[] //
)
{
    //
    bool result = false;

    //
    result =
        item.IsValid() &&
        HasChild(source);
    if (!result)
    {
        return result;
    }

    //
    int idx = -1;
    result = FindIndex(
        idx,
        item,
        source //
    );
    if (!result)
    {
        return result;
    }

    //
    result = ArrayRemove(
        source,
        idx,
        1 //
    );

    //
    return result;
}

/**
 * Remove Specified items from a Collection ...
 *
 * @param  items: Specified Items to Remove ...
 * @param  sources: Sepcified Collection for Removing items from it ...
 *
 * @return ( int )
 */
int Removes(
    XBoxZone &items[],
    XBoxZone &sources[] //
)
{
    //
    int result = 0;

    //
    bool has = HasChild(items) &&
               HasChild(sources);
    if (!has)
    {
        return result;
    }

    //
    int idx = -1;
    has = false;
    int count = ArraySize(items);
    for (int i = 0; i < count; i++)
    {
        //
        // Detect Item Index in Collection ...
        has = FindIndex(
            idx,
            items[i],
            sources //
        );
        if (has)
        {
            //
            // Remove Detected Index from Collection ...
            has = ArrayRemove(
                sources,
                idx,
                1 //
            );

            //
            if (has)
            {
                //
                // Count Removed Items ...
                result++;
            }
        }
    }

    //
    return result;
}

/**
 * Remove Specified items from a Collection ...
 *
 * @param  items: Specified Items to Remove ...
 * @param  sources: Sepcified Collection for Removing items from it ...
 *
 * @return ( int )
 */
int Removes(
    XPivot &items[],
    XPivot &sources[] //
)
{
    //
    int result = 0;

    //
    bool has = HasChild(items) &&
               HasChild(sources);
    if (!has)
    {
        return result;
    }

    //
    int idx = -1;
    has = false;
    int count = ArraySize(items);
    for (int i = 0; i < count; i++)
    {
        //
        // Detect Item Index in Collection ...
        has = FindIndex(
            idx,
            items[i],
            sources //
        );
        if (has)
        {
            //
            // Remove Detected Index from Collection ...
            has = ArrayRemove(
                sources,
                idx,
                1 //
            );

            //
            if (has)
            {
                //
                // Count Removed Items ...
                result++;
            }
        }
    }

    //
    return result;
}

/**
 * Remove Specified items from a Collection ...
 *
 * @param  items: Specified Items to Remove ...
 * @param  sources: Sepcified Collection for Removing items from it ...
 *
 * @return ( int )
 */
int Removes(
    XDirectionShift &items[],
    XDirectionShift &sources[] //
)
{
    //
    int result = 0;

    //
    bool has = HasChild(items) &&
               HasChild(sources);
    if (!has)
    {
        return result;
    }

    //
    int idx = -1;
    has = false;
    int count = ArraySize(items);
    for (int i = 0; i < count; i++)
    {
        //
        // Detect Item Index in Collection ...
        has = FindIndex(
            idx,
            items[i],
            sources //
        );
        if (has)
        {
            //
            // Remove Detected Index from Collection ...
            has = ArrayRemove(
                sources,
                idx,
                1 //
            );

            //
            if (has)
            {
                //
                // Count Removed Items ...
                result++;
            }
        }
    }

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Add If Not Exists                                                |
//+------------------------------------------------------------------+
/**
 * Add Specified Item to Specified Collection, if not Exists ...
 *
 * @param  item: Item to Add ...
 * @param  sources: Collection to Add item ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 *
 * @return ( bool )
 */
bool AddIfNotExists(
    XBoxZone &item,
    XBoxZone &sources[],
    int maxAllowed = 0 //
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    result = item.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Item Exists or not ...
    int idx = -1;
    bool isExists = FindIndex(
        idx,
        item,
        sources //
    );
    result = !isExists;
    if (!result)
    {
        return result;
    }

    //
    AddRef(
        item,
        sources //
    );

    //
    // Handle Cleanup Collection
    // if Max Reached ...
    if (result &&
        maxAllowed > 0)
    {
        //
        CleanupArray(
            sources,
            maxAllowed //
        );
    }

    //
    return result;
}

/**
 * Add Specified Item to Specified Collection, if not Exists ...
 *
 * @param  item: Item to Add ...
 * @param  sources: Collection to Add item ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 *
 * @return ( bool )
 */
bool AddIfNotExists(
    XPivot &item,
    XPivot &sources[],
    int maxAllowed = 0 //
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    result = item.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Item Exists or not ...
    int idx = -1;
    bool isExists = FindIndex(
        idx,
        item,
        sources //
    );
    result = !isExists;
    if (!result)
    {
        return result;
    }

    //
    AddRef(
        item,
        sources //
    );

    //
    // Handle Cleanup Collection
    // if Max Reached ...
    if (result &&
        maxAllowed > 0)
    {
        //
        CleanupArray(
            sources,
            maxAllowed //
        );
    }

    //
    return result;
}

/**
 * Add Specified Item to Specified Collection, if not Exists ...
 *
 * @param  item: Item to Add ...
 * @param  sources: Collection to Add item ...
 * @param  maxAllowed: int, Max Allowed Collection Size ...
 *
 * @return ( bool )
 */
bool AddIfNotExists(
    XDirectionShift &item,
    XDirectionShift &sources[],
    int maxAllowed = 0 //
)
{
    //
    bool result = false;

    //
    // Validate Args ...
    result = item.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Item Exists or not ...
    int idx = -1;
    bool isExists = FindIndex(
        idx,
        item,
        sources //
    );
    result = !isExists;
    if (!result)
    {
        return result;
    }

    //
    AddRef(
        item,
        sources //
    );

    //
    // Handle Cleanup Collection
    // if Max Reached ...
    if (result &&
        maxAllowed > 0)
    {
        //
        CleanupArray(
            sources,
            maxAllowed //
        );
    }

    //
    return result;
}

template <typename T>
int FillNotExists(
    T &dest[],
    T &source[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE,
    int maxAllowed = 0 //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    // Normalize ...
    maxAllowed = NormalizeInt(maxAllowed, 0);

    //
    // Validate ...
    bool has = HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has =
            source[i].IsValid() &&
            (!HasDirection(forDir) || forDir == source[i].dir);
        if (!has)
        {
            continue;
        }

        //
        has = AddIfNotExists(source[i], dest);
        if (has)
        {
            result++;
        }
    }

    //
    // Handle Cleanup Collection
    // if Max Reached ...
    if (maxAllowed > 0)
    {
        //
        CleanupArray(
            dest,
            maxAllowed //
        );
    }

    //
    return result;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Extract                                                          |
//+------------------------------------------------------------------+

/**
 * Extract Specified Directional Items ...
 *
 * @param  dest: reference collection to holds extracted items ...
 * @param  source: reference collection to provides data source ...
 * @param  forDir: Specified which direction to Extract items ...
 *
 * @return ( int )
 */
template <typename T>
int ExtractByDirection(
    T &dest[],
    T &source[],
    ENUM_X_DIRECTION forDir //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    // Validate ...
    bool has = HasChild(source) &&
               HasDirection(forDir);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = source[i].dir == forDir;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}
//+------------------------------------------------------------------+

//
//
//

//+------------------------------------------------------------------+
//| Other Extensions                                                 |
//+------------------------------------------------------------------+

/**
 * Validate Boxes ...
 * @param  box: XBoxZone instance reference for Validate ...
 * @param  barIndex: int, Bar Index ...
 * @param  barsLength: int, Bars Length for Validation ...
 *
 * @return ( bool )
 */
bool IsBoxValid(
    XBoxZone &box,
    int barIndex = 0,
    int barsLength = 3 //
)
{
    //
    bool result = false;

    //
    // Normalize Bar Index ...
    barIndex = NormalizeInt(barIndex, 0);
    barsLength = NormalizeInt(barsLength, 1);

    //
    // Ceck Input Box is Valid ...
    result = box.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Input Box is Breaked or not ...
    // Breaktion Found using Counting Over/Under Close Bars ...
    bool isBullish = box.IsBullish();
    double appliedPrice = box.GetInDirectionalPrice();
    result = NotEmptyZero(appliedPrice);
    if (!result)
    {
        return result;
    }

    //
    int toIDX = box.ToIndex();

    //
    XOHCL iBar;
    int edgeBreakes = 0;
    for (int i = barIndex; i <= toIDX; i++)
    {
        //
        // Initialize Indexed Bar ...
        result = iBar.Init(
            box.symbol,
            box.period,
            i //
        );

        //
        // Checking Price ...
        result =
            //
            // Checking Bar Validation ...
            result &&
            //
            // Checking Price Breaked or not ...
            (isBullish
                 ? iBar.close < box.lower
                 : iBar.close > box.upper);
        if (result)
        {
            edgeBreakes++;
        }
        else
        {
            edgeBreakes = 0;
        }

        //
        result = edgeBreakes >= barsLength;
        if (result)
        {
            break;
        }

        //
        iBar.Clean();
    }

    //
    iBar.Clean();

    //
    // return true;

    //
    result =
        edgeBreakes < barsLength;

    //
    return result;
}

//
void SortBoxes(
    XBoxZone &items[],
    int maxAllowed = 0 //
)
{
    //
    int idx = -1;
    bool has = false;

    //
    has = HasChild(items);
    if (!has)
    {
        return;
    }

    //
    XBoxZone tmp[];
    Copy(
        items,
        tmp //
    );
    Clean(items);

    //
    while (HasChild(tmp))
    {
        //
        idx = GetOldest(tmp);
        has = IsValidIndex(idx);
        if (!has)
        {
            break;
        }

        //
        XBoxZone iBox = tmp[idx];
        ArrayRemove(
            tmp,
            idx,
            1 //
        );

        //
        AddRef(
            iBox,
            items //
        );
    }

    //
    if (maxAllowed > 0)
    {
        //
        CleanupArray(
            items,
            maxAllowed //
        );
    }

    //
    Clean(tmp);
}

/**
 * Check a BaxHas Valid Place for Specified Bar ...
 *
 * @param bar: XOHCL instance reference, Specified Bar ...
 * #param box: XBoxZone instance reference, Specified Box ...
 *
 * @return ( bool )
 */
bool IsBoxPlaceValidForBar(
    XOHCL &bar,
    XBoxZone &box //
)
{
    //
    bool result = false;

    //
    result = bar.IsValid() &&
             box.IsValid();
    if (!result)
    {
        return result;
    }

    //
    result =
        box.IsBullish()
            ? bar.low > box.upper
            : bar.high < box.upper;

    //
    return result;
}

//
void ValidateBoxes(
    XBoxZone &boxes[],
    int barIndex = 0,
    int barsLength = 3,
    bool forceSorting = false,
    int maxAllowed = 0 //
)
{
    //
    bool has = false;

    //
    // Normalize Args ...
    barIndex = NormalizeInt(barIndex, 0);
    barsLength = NormalizeInt(barsLength, 3);

    //
    // Validate Args ...
    has = HasChild(boxes);
    if (!has)
    {
        return;
    }

    //
    // Copy Boxes to tmp ...
    XBoxZone tmp[];
    Copy(
        boxes,
        tmp //
    );
    Clean(boxes);

    //
    // Looping Through Exists Boxes ...
    while (HasChild(tmp))
    {
        //
        // Select Indexed Box ...
        XBoxZone iBox = tmp[0];

        //
        // Remove Indexed Box ...
        ArrayRemove(
            tmp,
            0,
            1 //
        );

        //
        // Check Box Validating ...
        has = IsBoxValid(
            iBox,
            barIndex,
            barsLength //
        );
        if (has)
        {
            //
            AddRef(
                iBox,
                boxes //
            );
        }
    }

    //
    // Sorting ...
    if (forceSorting)
    {
        //
        // Check Has Result or not ...
        has = HasChild(boxes);
        if (has)
        {
            //
            // Sorting ...
            SortBoxes(
                boxes,
                maxAllowed //
            );
        }
    }

    //
}

void UpdateToTime(
    XBoxZone &zones[],
    datetime toTime = NULL //
)
{
    //
    // Normalize ...
    toTime = NormalizeTime(toTime);

    //
    // Validate ...
    bool has = HasChild(zones);
    if (!has)
    {
        return;
    }

    //
    int count = ArraySize(zones);
    for (int i = 0; i < count; i++)
    {
        zones[i].to = toTime;
    }
}

/**
 * Remove Breaked Zones from a Collection ...
 *
 * @param  source: XBoxZone, reference to Specified Collection ...
 *
 * @return ( int )
 */
int RemoveBreakedZones(
    XBoxZone &source[],
    bool forceBreak = false //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    XBoxZone iZone;
    XBoxZone tmps[];
    Copy(
        source,
        tmps //
    );
    XClean(source);
    XOHCL tmpBars[];
    int breakerBars = 0;
    while (HasChild(tmps))
    {
        //
        iZone.Clean();
        iZone = tmps[0];
        ArrayRemove(tmps, 0, 1);
        has = IsXValid(iZone.breakAt);
        if (has)
        {
            continue;
        }

        //
        if (forceBreak)
        {
            //
            breakerBars = GetBreakerBars(iZone, tmpBars);
            has = IsValidSize(breakerBars);
        }
        if (has)
        {
            continue;
        }

        //
        AddIfNotExists(
            iZone,
            source //
        );
    }

    //
    result = ArraySize(source);

    //
    return result;
}

/**
 * Remove Filled Zones ...
 *
 * @param  source: XBoxZone, collection reference to Provide Data Source ...
 * @param  zoneRangeFilledFactor: double, Filling Factor ...
 *
 * @return ( int )
 */
int RemoveFilledZones(
    XBoxZone &source[],
    double zoneRangeFilledFactor = 0.8 //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    XBoxZone iTMP;
    XBoxZone tmp[];
    Copy(source, tmp);
    XClean(source);
    while (HasChild(tmp))
    {
        //
        iTMP.Clean();
        iTMP = tmp[0];
        ArrayRemove(tmp, 0, 1);

        //
        has = IsFilled(iTMP, zoneRangeFilledFactor);
        if (!has)
        {
            AddIfNotExists(iTMP, source);
        }
    }

    //
    result = ArraySize(source);

    //
    return result;
}

// #region Zone Converters ...
/**
 * Converts a Bar to Pivot ...
 *
 * @param  bar: XOHCL, reference to Specified Bar ...
 * @param  pivot: XPivot, reference to holds result ...
 * @param  forDir: ENUM_X_DIRECTION, specified Pivot Direction ...
 * @param  prefix: string, Specified Pivot Prefix (used for Object Type) ...
 * @param  peakPriceType: ENUM_X_PRICE, Specified Peak Pivot's Applied Price ...
 * @param  valePriceType: ENUM_X_PRICE, Specified Vale Pivot's Applied Price ...
 *
 * @return ( bool )
 */
bool ToPivot(
    XOHCL &bar,
    XPivot &pivot,
    ENUM_X_DIRECTION forDir,
    string prefix = NULL,
    ENUM_X_PRICE peakPriceType = X_PRICE_HIGH,
    ENUM_X_PRICE valePriceType = X_PRICE_LOW //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    pivot.Clean();

    //
    // Normalize ...
    if (!IsXValid(peakPriceType))
    {
        peakPriceType = X_PRICE_HIGH;
    }
    if (!IsXValid(valePriceType))
    {
        valePriceType = X_PRICE_LOW;
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             HasDirection(forDir) &&
             IsXValid(peakPriceType) &&
             IsXValid(valePriceType);
    if (!result)
    {
        return result;
    }

    //
    // Check Direction ...
    bool isBullish = IsSpecifiedBullish(forDir);

    //
    // Prepare Value of Pivot ...
    double iValue =
        isBullish
            ? bar.GetPrice(valePriceType)
            : bar.GetPrice(peakPriceType);

    //
    // Prepare Pivot Type ...
    ENUM_X_PIVOT_TYPE iType =
        isBullish
            ? X_PIVOT_TYPE_VALE
            : X_PIVOT_TYPE_PEAK;

    //
    // Initialize Pivot ...
    result = pivot.Init(
        iValue,
        bar.time,
        bar.symbol,
        forDir,
        bar.period,
        iType //
    );
    if (result)
    {
        //
        // Set Prefix if Pivot initialized successfully ...
        pivot.prefix = prefix;
    }

    //
    // Validate ...
    result = pivot.IsValid();
    if (!result)
    {
        pivot.Clean();
    }

    //
    return result;
}

/**
 * Converts a Bar to Pattern Pivot ...
 *
 * @param  bar: XOHCL, reference to Specified Bar ...
 * @param  pivot: XPivot, reference to holds result ...
 * @param  patternDir: ENUM_X_DIRECTION, specified Pivot Direction ...
 * @param  pattern: ENUM_X_BAR_PATTERN, Specified Pattern Type ...
 * @param  peakPriceType: ENUM_X_PRICE, Specified Peak Pivot's Applied Price ...
 * @param  valePriceType: ENUM_X_PRICE, Specified Vale Pivot's Applied Price ...
 *
 * @return ( bool )
 */
bool ToPivot(
    XOHCL &bar,
    XPivot &pivot,
    ENUM_X_DIRECTION patternDir,
    ENUM_X_BAR_PATTERN pattern,
    ENUM_X_PRICE peakPriceType = X_PRICE_HIGH,
    ENUM_X_PRICE valePriceType = X_PRICE_LOW //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    pivot.Clean();

    //
    // Normalize ...
    if (!IsXValid(peakPriceType))
    {
        peakPriceType = X_PRICE_HIGH;
    }
    if (!IsXValid(valePriceType))
    {
        valePriceType = X_PRICE_LOW;
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             IsXValid(pattern) &&
             IsXValid(peakPriceType) &&
             IsXValid(valePriceType) &&
             HasDirection(patternDir);
    if (!result)
    {
        return result;
    }

    //
    datetime iTime = bar.time;
    string iSymbol = bar.symbol;
    string iPrefix = ToXString(pattern);
    ENUM_TIMEFRAMES iPeriod = bar.period;
    double iValue = IsXBullish(patternDir)
                        ? bar.GetPrice(X_PRICE_LOW)
                    : IsXBearish(patternDir)
                        ? bar.GetPrice(X_PRICE_HIGH)
                        : EMPTY_VALUE;
    ENUM_X_PIVOT_TYPE iType = IsXBullish(patternDir)
                                  ? X_PIVOT_TYPE_VALE
                              : IsXBearish(patternDir)
                                  ? X_PIVOT_TYPE_PEAK
                                  : X_PIVOT_TYPE_NONE;

    //
    result = pivot.Init(
        iValue,
        iTime,
        iSymbol,
        patternDir,
        iPeriod,
        iType //
    );
    if (result)
    {
        pivot.prefix = iPrefix;
    }

    //
    // Cleanup Resources ...
    if (!result)
    {
        pivot.Clean();
    }

    //
    return result;
}

/**
 * Converts POI Bar to Box ...
 *
 * @param  bar: XOHCL, reference to Specified Pattern ...
 * @param  box: XBoxZone, reference to holds result ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Pattern Direction ...
 * @param  prefix: string, Specified Pattern Type ...
 * @param  peakPriceType: ENUM_X_PRICE, Specified Peak Pivot's Applied Price ...
 * @param  valePriceType: ENUM_X_PRICE, Specified Vale Pivot's Applied Price ...
 * @param  setFromByBreakerBar: bool, Set Box From Time to Breaker Bar ...
 *
 * @return ( bool )
 */
bool ToBox(
    XOHCL &bar,
    XBoxZone &box,
    ENUM_X_DIRECTION forDir,
    string prefix = NULL,
    ENUM_X_PRICE peakPriceType = X_PRICE_HIGH,
    ENUM_X_PRICE valePriceType = X_PRICE_LOW,
    bool setFromByBreakerBar = false //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    if (!IsXValid(peakPriceType))
    {
        peakPriceType = X_PRICE_HIGH;
    }
    if (!IsXValid(valePriceType))
    {
        valePriceType = X_PRICE_LOW;
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             HasDirection(forDir) &&
             IsXValid(peakPriceType) &&
             IsXValid(valePriceType);
    if (!result)
    {
        return result;
    }

    //
    // Check if is Valid Pattern ...
    ENUM_X_BAR_PATTERN iPattern = ToBarPattern(prefix);
    result = IsXValid(iPattern);
    if (result)
    {
        //
        // Conver to Box as Pattern ...
        result = ToBox(
            bar,
            box,
            forDir,
            iPattern,
            peakPriceType,
            valePriceType,
            setFromByBreakerBar //
        );
    }
    //
    // There is not any Valid Pattern Provided ...
    else
    {
        //
        bool isBullish = IsXBullish(forDir);
        double peakPrice = bar.GetPrice(peakPriceType);
        double valePrice = bar.GetPrice(valePriceType);

        //
        string iType = prefix;
        datetime iAt = bar.time;
        datetime iTo = bar.time;
        double iUpper = peakPrice;
        double iLower = valePrice;
        string iSymbol = bar.symbol;
        datetime iFrom = bar.BeforeOn();
        ENUM_TIMEFRAMES iPeriod = bar.period;

        //
        // Assign Values to Box ...
        box.at = iAt;
        box.to = iTo;
        box.from = iFrom;
        box.type = iType;
        box.dir = forDir;
        box.upper = iUpper;
        box.lower = iLower;
        box.symbol = iSymbol;
        box.period = iPeriod;
    }

    //
    // Validate result ...
    result = box.IsValid();
    if (!result)
    {
        //
        box.Clean();
        return result;
    }

    //
    return result;
}

/**
 * Converts Pattern Bar to Box ...
 *
 * @param  bar: XOHCL, reference to Specified Pattern ...
 * @param  box: XBoxZone, reference to holds result ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Pattern Direction ...
 * @param  prefix: string, Specified Pattern Type ...
 * @param  peakPriceType: ENUM_X_PRICE, Specified Peak Pivot's Applied Price ...
 * @param  valePriceType: ENUM_X_PRICE, Specified Vale Pivot's Applied Price ...
 * @param  setFromByBreakerBar: bool, Set Box From Time to Breaker Bar ...
 *
 * @return ( bool )
 */
bool ToBox(
    XOHCL &bar,
    XBoxZone &box,
    ENUM_X_DIRECTION patternDir,
    ENUM_X_BAR_PATTERN pattern,
    ENUM_X_PRICE peakPriceType = X_PRICE_HIGH,
    ENUM_X_PRICE valePriceType = X_PRICE_LOW,
    bool setFromByBreakerBar = false //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    if (!IsXValid(peakPriceType))
    {
        peakPriceType = X_PRICE_HIGH;
    }
    if (!IsXValid(valePriceType))
    {
        valePriceType = X_PRICE_LOW;
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             IsXValid(pattern) &&
             IsXValid(peakPriceType) &&
             IsXValid(valePriceType) &&
             HasDirection(patternDir);
    if (!result)
    {
        return result;
    }

    //
    // Define Requirements ...
    XOHCL tmpBar;
    XBoxZone tmpBox;
    bool has = false;
    datetime iAt = bar.time;
    datetime iTo = bar.time;
    double iUpper = EMPTY_VALUE;
    double iLower = EMPTY_VALUE;
    string iSymbol = bar.symbol;
    datetime iFrom = bar.BeforeOn();
    string iType = ToXString(pattern);
    ENUM_TIMEFRAMES iPeriod = bar.period;

    //
    bool isBullish = IsXBullish(patternDir);
    double peakPrice = bar.GetPrice(peakPriceType);
    double valePrice = bar.GetPrice(valePriceType);

    //
    // Calculate Requirements Based on Pattern Type and Direction ...
    switch (pattern)
    {
    //
    case X_BAR_PATTERN_HIGH: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            has = GetBreakerBar(
                bar,
                tmpBar,
                peakPriceType,
                peakPriceType,
                X_DIRECTION_BULLISH //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = peakPrice;
        iLower = bar.GetUp();
    }
    break;

    //
    case X_BAR_PATTERN_LOW: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            has = GetBreakerBar(
                bar,
                tmpBar,
                valePriceType,
                valePriceType,
                X_DIRECTION_BEARISH //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = bar.GetDown();
        iLower = valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_MOMENTUM: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = peakPrice;
        iLower = valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_ENGULFED: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = peakPrice;
        iLower = valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_TRUE_GAPED: {
        //
        tmpBar.Clean();
        has = bar.GetPreviousBar(tmpBar);
        if (has)
        {
            //
            iFrom = tmpBar.time;

            //
            iUpper =
                isBullish
                    ? valePrice
                    : tmpBar.GetPrice(valePriceType);
            iLower =
                isBullish
                    ? tmpBar.GetPrice(peakPriceType)
                    : peakPrice;
        }
    }
    break;

    //
    case X_BAR_PATTERN_PINNED: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper =
            isBullish
                ? bar.GetDown()
                : peakPrice;
        iLower =
            isBullish
                ? valePrice
                : bar.GetUp();
    }
    break;

    //
    case X_BAR_PATTERN_STAR: {
        //
        tmpBar.Clean();
        ENUM_X_PRICE iPType = isBullish
                                  ? valePriceType
                                  : peakPriceType;
        has = GetBreakerBar(
            bar,
            tmpBar,
            iPType,
            iPType,
            patternDir //
        );
        if (has)
        {
            //
            if (setFromByBreakerBar)
            {
                iFrom = tmpBar.time;
            }

            //
            int idx = -1;
            int mLength = tmpBar.Index() - bar.Index();
            iLower = bar.FindLowest(idx, mLength, valePriceType);
            iUpper = bar.FindHighest(idx, mLength, peakPriceType);
        }
    }
    break;

    //
    case X_BAR_PATTERN_PIERCING: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = peakPrice;
        iLower = valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_RISING: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper = peakPrice;
        iLower = valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_FLAG: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        int idx = -1;
        int mLength = 3;
        iUpper = bar.FindHighest(idx, mLength, peakPriceType);
        iLower = bar.FindLowest(idx, mLength, valePriceType);
    }
    break;

    //
    case X_BAR_PATTERN_SIGNALKEY: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        has = bar.GetPreviousBar(tmpBar);
        if (has)
        {
            //
            iUpper = MathMax(peakPrice, tmpBar.GetPrice(peakPriceType));
            iLower = MathMin(valePrice, tmpBar.GetPrice(valePriceType));
        }
    }
    break;

    //
    case X_BAR_PATTERN_OB: {
        //
        tmpBox.Clean();
        has = ToOBBox(
            bar,
            tmpBox,
            patternDir //
        );
        if (has)
        {
            //
            iFrom = tmpBox.from;
            iUpper = tmpBox.upper;
            iLower = tmpBox.lower;
        }
    }
    break;

    //
    case X_BAR_PATTERN_FVG: {
        //
        tmpBox.Clean();
        has = ToFVGBox(
            bar,
            tmpBox,
            patternDir //
        );
        if (has)
        {
            //
            iFrom = tmpBox.from;
            iUpper = tmpBox.upper;
            iLower = tmpBox.lower;
        }
    }
    break;

    //
    case X_BAR_PATTERN_SUPPORT: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper =
            isBullish
                ? peakPrice
                : valePrice;
        iLower =
            isBullish
                ? peakPrice
                : valePrice;
    }
    break;

    //
    case X_BAR_PATTERN_RESISTANCE: {
        //
        if (setFromByBreakerBar)
        {
            //
            tmpBar.Clean();
            ENUM_X_PRICE iPType = isBullish
                                      ? valePriceType
                                      : peakPriceType;
            has = GetBreakerBar(
                bar,
                tmpBar,
                iPType,
                iPType,
                patternDir //
            );
            if (has)
            {
                iFrom = tmpBar.time;
            }
        }

        //
        iUpper =
            isBullish
                ? peakPrice
                : valePrice;
        iLower =
            isBullish
                ? peakPrice
                : valePrice;
    }
    break;
    }

    //
    // Assign Values to Box ...
    box.at = iAt;
    box.to = iTo;
    box.from = iFrom;
    box.type = iType;
    box.upper = iUpper;
    box.lower = iLower;
    box.symbol = iSymbol;
    box.period = iPeriod;
    box.dir = patternDir;

    //
    result = box.IsValid();

    //
    // Cleanup Resources ...
    if (!result)
    {
        box.Clean();
    }
    tmpBar.Clean();
    tmpBox.Clean();

    //
    return result;
}

/**
 * Converts Order Flow to Zone ...
 *
 * @param  zone: XBoxZone, reference to holds result ...
 * @param  orderFlow: XBoxZone, collection reference to provides order flow ...
 *
 * @return ( bool )
 */
bool ToBox(
    XBoxZone &zone,
    XBoxZone &orderFlow[] //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    zone.Clean();

    //
    // Validate ...
    result = HasChild(orderFlow);
    if (!result)
    {
        return result;
    }

    //
    zone = orderFlow[0];
    int idx = GetHighest(orderFlow);
    if (IsValidIndex(idx))
    {
        zone.upper = orderFlow[idx].upper;
    }
    idx = GetLowest(orderFlow);
    if (IsValidIndex(idx))
    {
        zone.lower = orderFlow[idx].lower;
    }

    //
    zone.type = "XOrderFlowP";

    //
    result = zone.IsValid();

    //
    return result;
}

/**
 * Converts a Bar to OB Box ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  box: XBoxZone, destination Box ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( bool )
 */
bool ToOBBox(
    XOHCL &bar,
    XBoxZone &box,
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    if (!HasDirection(forDir))
    {
        forDir = bar.GetDirection();
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             HasDirection(forDir) &&
             bar.GetDirection() == forDir;
    if (!result)
    {
        return result;
    }

    //
    // Detect OB Start Bar ...
    XOHCL obStartBar;
    result = GetDirectionalBar(
        bar,
        obStartBar,
        Opposit(forDir) //
    );
    if (!result)
    {
        //
        obStartBar.Clean();
        return result;
    }

    //
    box.dir = forDir;
    box.to = bar.time;
    box.at = bar.time;
    box.symbol = bar.symbol;
    box.period = bar.period;
    box.from = obStartBar.time;
    box.type = ToXString(X_BAR_PATTERN_OB);
    box.lower = MathMin(obStartBar.low, bar.low);
    box.upper = MathMax(obStartBar.high, bar.high);

    //
    result = box.IsValid();

    //
    // Cleanup Resources ...
    if (!result)
    {
        box.Clean();
    }
    obStartBar.Clean();

    //
    return result;
}

/**
 * Converts a Bar to FVG Box ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  box: XBoxZone, destination Box ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Direction ...
 *
 * @return ( bool )
 */
bool ToFVGBox(
    XOHCL &bar,
    XBoxZone &box,
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    if (!HasDirection(forDir))
    {
        forDir = bar.GetDirection();
    }

    //
    // Validate ...
    result = bar.IsValid() &&
             HasDirection(forDir) &&
             bar.GetDirection() == forDir;
    if (!result)
    {
        return result;
    }

    //
    // Detect FVG Start Bar ...
    XOHCL fvgStartBar;
    result = bar.BarAt(
        bar.Index() + 2,
        fvgStartBar);
    if (!result)
    {
        //
        fvgStartBar.Clean();
        return result;
    }

    //
    bool isBullish = IsXBullish(forDir);

    //
    box.dir = forDir;
    box.to = bar.time;
    box.at = bar.time;
    box.upper =
        isBullish
            ? bar.low
            : bar.high;
    box.symbol = bar.symbol;
    box.period = bar.period;
    box.lower =
        isBullish
            ? fvgStartBar.high
            : fvgStartBar.low;
    box.from = fvgStartBar.time;
    box.type = ToXString(X_BAR_PATTERN_FVG);

    //
    result = box.IsValid();

    //
    // Cleanup Resources ...
    if (!result)
    {
        box.Clean();
    }
    fvgStartBar.Clean();

    //
    return result;
}

/**
 * Converts a Bar to Support Box ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  box: XBoxZone, destination Box ...
 * @param  toTime: datetime, Specified Box to Time ...
 *
 * @return ( bool )
 */
bool ToSupportBox(
    XOHCL &bar,
    XBoxZone &box,
    datetime toTime = NULL,
    int validationLength = 0 //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    toTime = NormalizeTime(toTime);
    validationLength = NormalizeInt(validationLength, 0);

    //
    // Validate ...
    result = bar.IsValid();
    if (!result)
    {
        return result;
    }

    //
    XOHCL breaker;
    result = GetBreakerBar(
        bar,
        breaker,
        X_PRICE_LOW,
        X_PRICE_DOWN,
        X_DIRECTION_BEARISH,
        500 //
    );
    if (!result)
    {
        breaker.Clean();
    }

    //
    box.to = toTime;
    box.at = bar.time;
    box.from =
        breaker.IsValid()
            ? breaker.time
        : validationLength <= 0
            ? bar.time
            : GetBarTime(
                  bar.symbol,
                  bar.period,
                  bar.Index() + validationLength //
              );
    box.lower = bar.low;
    box.symbol = bar.symbol;
    box.period = bar.period;
    box.upper = bar.GetDown();
    box.dir = X_DIRECTION_BULLISH;
    box.type = ToXString(X_BAR_PATTERN_SUPPORT);

    //
    result = box.IsValid();
    if (!result)
    {
        box.Clean();
    }

    //
    return result;
}

/**
 * Converts a Bar to Resistance Box ...
 *
 * @param  bar: XOHCL, Specified Bar ...
 * @param  box: XBoxZone, destination Box ...
 * @param  toTime: datetime, Specified Box to Time ...
 *
 * @return ( bool )
 */
bool ToResistanceBox(
    XOHCL &bar,
    XBoxZone &box,
    datetime toTime = NULL,
    int validationLength = 0 //
)
{
    //
    bool result = false;

    //
    // Prepare ...
    box.Clean();

    //
    // Normalize ...
    toTime = NormalizeTime(toTime);
    validationLength = NormalizeInt(validationLength, 0);

    //
    // Validate ...
    result = bar.IsValid();
    if (!result)
    {
        return result;
    }

    //
    XOHCL breaker;
    result = GetBreakerBar(
        bar,
        breaker,
        X_PRICE_HIGH,
        X_PRICE_UP,
        X_DIRECTION_BULLISH,
        500 //
    );
    if (!result)
    {
        breaker.Clean();
    }

    //
    box.to = toTime;
    box.at = bar.time;
    box.from =
        breaker.IsValid()
            ? breaker.time
        : validationLength <= 0
            ? bar.time
            : GetBarTime(
                  bar.symbol,
                  bar.period,
                  bar.Index() + validationLength //
              );
    box.upper = bar.high;
    box.symbol = bar.symbol;
    box.period = bar.period;
    box.lower = bar.GetUp();
    box.dir = X_DIRECTION_BEARISH;
    box.type = ToXString(X_BAR_PATTERN_RESISTANCE);

    //
    result = box.IsValid();
    if (!result)
    {
        box.Clean();
    }

    //
    return result;
}
// #endregion

// #region Zone Checkers ...
/**
 * Check a Zone is Filled or Not ...
 *
 * @param  zone: XBoxZone, reference to Specified Zone ...
 * @param  zoneRangeFilledFactor: double, Filling Factor ...
 *
 * @return ( bool )
 */
bool IsFilled(
    XBoxZone &zone,
    double zoneRangeFilledFactor = 0.8 //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = zone.IsValid() &&
             zoneRangeFilledFactor > 0 &&
             zoneRangeFilledFactor < 1;
    if (!result)
    {
        //
        result = true;
        return result;
    }

    //
    XOHCL iBar;
    int toIDX = zone.ToIndex();
    int fromIDX = zone.FromIndex();
    double zoneRangeFactor = zone.GetRange() / 100;
    double useValue = zone.lower + (zoneRangeFactor * zoneRangeFilledFactor);
    for (int i = toIDX; i < fromIDX; i++)
    {
        //
        // Initialize Indexed Bar ...
        iBar.Clean();
        result = iBar.Init(zone.symbol, zone.period, i);
        result =
            result &&
            (zone.IsBullish()
                 ? iBar.GetPrice(X_PRICE_LOW) <= useValue
                 : iBar.GetPrice(X_PRICE_HIGH) >= useValue);
        if (result)
        {
            break;
        }
    }

    //
    // Cleanup ...
    iBar.Clean();

    //
    return result;
}

/**
 * Check Specified Zone is Breaked by Specified Bar or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 * @param  forceBarEdge: bool, flag to Check Breaktion by high ot low ...
 *
 * @return ( bool )
 */
bool IsBreaked(
    XOHCL &bar,
    XBoxZone &zone,
    bool forceBarEdge = false //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Break ...
    result =
        zone.IsBullish()
            ? bar.close < zone.lower
            : bar.close > zone.upper;

    //
    // Apply Force Bar Edge ...
    if (forceBarEdge)
    {
        //
        result =
            result &&
                    zone.IsBullish()
                ? bar.low > zone.upper
                : bar.high < zone.lower;
    }

    //
    return result;
}

/**
 * Check Specified Zone is Tested by Specified Bar or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 *
 * @return ( bool )
 */
bool IsTested(
    XOHCL &bar,
    XBoxZone &zone //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Test ...
    result =
        zone.IsBullish()
            ? bar.low < zone.upper && bar.low > zone.lower
            : bar.high > zone.lower &&
                  bar.high < zone.upper;

    //
    return result;
}

/**
 * Check Specified Zone is Rejected by Specified Bar or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 *
 * @return ( bool )
 */
bool IsRejected(
    XOHCL &bar,
    XBoxZone &zone //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Test ...
    result =
        IsTested(bar, zone) &&
        (zone.IsBullish()
             ? bar.GetDown() >= zone.upper
             : bar.GetUp() <= zone.lower);

    //
    return result;
}

/**
 * Check Specified Bar is Inside Specified Zone or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 *
 * @return ( bool )
 */
bool IsInside(
    XOHCL &bar,
    XBoxZone &zone //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Inside ...
    result =
        zone.IsBullish()
            ? bar.low < zone.upper &&
                  bar.low > zone.lower
            : bar.high > zone.lower &&
                  bar.high < zone.upper;

    //
    return result;
}

/**
 * Check Specified Bar is Acted On Specified Zone or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 *
 * @return ( bool )
 */
bool IsActed(
    XOHCL &bar,
    XBoxZone &zone //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Get Previous Bar ...
    XOHCL pBar;
    result = bar.GetPreviousBar(pBar);
    if (!result)
    {
        return result;
    }

    //
    // Previous Bar must one of Following States:
    // - Tested;
    // - Inside;
    // - Breaked;
    bool isTested = IsTested(pBar, zone);
    bool isInside = IsInside(pBar, zone);
    bool isBreaked = IsBreaked(pBar, zone);
    result = isTested ||
             isInside ||
             isBreaked;
    if (!result)
    {
        return result;
    }

    //
    // Current Bar Must Close Over / Under Zone ...
    result =
        zone.IsBullish()
            ? bar.close > zone.upper
        : zone.IsBearish()
            ? bar.close < zone.lower
            : false;

    //
    // Cleanup Resources ...
    pBar.Clean();

    //
    return result;
}

/**
 * Check Specified Zone is Breaked or not ...
 *
 * @param  zone: XBoxZone, reference to Specified Zone ...
 * @param  priceType: ENUM_X_PRICE, Specified Price Type for Zone Break Detection ...
 *
 * @return ( bool )
 */
bool IsBreaked(
    XBoxZone &zone,
    ENUM_X_PRICE priceType = X_PRICE_CLOSE //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = zone.IsValid() &&
             IsXValid(priceType);
    if (!result)
    {
        //
        result = true;
        return result;
    }

    //
    XOHCL iBar;
    double iPrice;
    bool isBullish;
    int start = zone.ToIndex();
    int end = zone.FromIndex();
    for (int i = start; i < end; i++)
    {
        //
        // Initialize Indexed Bar ...
        iBar.Clean();
        result = iBar.Init(zone.symbol, zone.period, i);
        if (!result)
        {
            //
            result = true;
            break;
        }

        //
        isBullish = zone.IsBullish();
        iPrice = iBar.GetPrice(priceType);
        result = isBullish
                     ? iPrice < zone.lower
                     : iPrice > zone.upper;
        if (result)
        {
            break;
        }
    }

    //
    // Cleanup ...
    iBar.Clean();

    //
    return result;
}

/**
 * Check Specified Bar is Acted On Specified Zone or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 * @param  validationLength: Specified Length for Breaks ...
 *
 * @return ( bool )
 */
bool IsValidBreaked(
    XOHCL &bar,
    XBoxZone &zone,
    int validationLength = 3 //
)
{
    //
    bool result = false;

    //
    // Normalize ...
    validationLength = NormalizeInt(validationLength, 1);

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    XOHCL iBar;
    int count = 0;
    int start = bar.Index();
    int end = start + validationLength;
    for (int i = start; i < end; i++)
    {
        //
        // Initialize Indexed Bar ...
        result = bar.BarAt(i, iBar);
        if (!result)
        {
            break;
        }

        //
        result = IsBreaked(iBar, zone);
        if (result)
        {
            count++;
        }
    }

    //
    result = IsValidSize(count) &&
             count >= validationLength;

    //
    // Cleanup Resources ...
    iBar.Clean();

    //
    return result;
}

/**
 * Check Specified Bar is Fake Breaked Specified Zone or not ...
 *
 * @param  bar: Specified Bar ...
 * @param  zone: Specified Zone ...
 * @param  validationLength: Specified Length for Breaks ...
 *
 * @return ( bool )
 */
bool IsFakeBreaked(
    XOHCL &bar,
    XBoxZone &zone,
    int validationLength = 3 //
)
{
    //
    bool result = false;

    //
    // Normalize ...
    validationLength = NormalizeInt(validationLength, 1);

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    // Check Bar Must Acting Zone ...
    result = zone.IsBullish()
                 ? bar.close > zone.upper
             : zone.IsBearish()
                 ? bar.close < zone.lower
                 : false;
    if (!result)
    {
        return result;
    }

    //
    // Detect Breaker Bars ...
    XOHCL breakerBars[];
    int breakerBarsCount = GetBreakerBars(
        zone,
        breakerBars //
    );
    result = IsValidSize(breakerBarsCount);
    if (!result)
    {
        //
        XClean(breakerBars);
        return result;
    }

    //
    // Get Youngest Breaker Bars ...
    int idx = GetYoungest(breakerBars);
    result = IsValidIndex(idx);
    if (!result)
    {
        //
        XClean(breakerBars);
        return result;
    }

    //
    result = breakerBars[idx].Index() - bar.Index() <= validationLength;

    //
    // Cleanup Resources ...
    XClean(breakerBars);

    //
    return result;
}

/**
 * Check Specified ZOne is Affected by Specified Bar or not ...
 *
 * @param  bar: XOHCL, reference to Specified Bar ...
 * @param  zone: XBoxZone, reference to Specified Zone ...
 *
 * @return ( bool )
 */
bool IsZoneAffected(
    XOHCL &bar,
    XBoxZone &zone //
)
{
    //
    bool result = false;

    //
    // Validate ...
    result = bar.IsValid() &&
             zone.IsValid();
    if (!result)
    {
        return result;
    }

    //
    bool isActed = IsActed(bar, zone);
    bool isTested = IsTested(bar, zone);
    bool isFakeBreaked = IsFakeBreaked(bar, zone);

    //
    result = isActed ||
             isTested ||
             isFakeBreaked;

    //
    return result;
}
// #endregion

// #region Zones Hasers ...
/**
 * Check a Bar is Inside a Zone or not ...
 *
 * @param  index: int, reference to holds detected index ...
 * @param  bar: reference to Specified Bar ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( bool )
 */
bool HasInside(
    int &index,
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    XBoxZone selected[];
    int count = ExtractInsides(bar, selected, source);
    bool result = IsValidSize(count);
    if (result)
    {
        //
        index = GetYoungest(selected);
        if (IsValidIndex(index))
        {
            result = FindIndex(index, selected[index], source);
        }
    }

    //
    XClean(selected);

    //
    return result;
}

/**
 * Check a Bar is Reject a Zone or not ...
 *
 * @param  index: int, reference to holds detected index ...
 * @param  bar: reference to Specified Bar ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( bool )
 */
bool HasRejected(
    int &index,
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    XBoxZone selected[];
    int count = ExtractRejecteds(bar, selected, source);
    bool result = IsValidSize(count);
    if (result)
    {
        //
        index = GetYoungest(selected);
        if (IsValidIndex(index))
        {
            result = FindIndex(index, selected[index], source);
        }
    }

    //
    XClean(selected);

    //
    return result;
}

/**
 * Check a Bar is Break a Zone or not ...
 *
 * @param  index: int, reference to holds detected index ...
 * @param  bar: reference to Specified Bar ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( bool )
 */
bool HasBreaked(
    int &index,
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    XBoxZone selected[];
    int count = ExtractBreakeds(bar, selected, source);
    bool result = IsValidSize(count);
    if (result)
    {
        //
        index = GetYoungest(selected);
        if (IsValidIndex(index))
        {
            result = FindIndex(index, selected[index], source);
        }
    }

    //
    XClean(selected);

    //
    return result;
}

/**
 * Check a Bar is Act a Zone or not ...
 *
 * @param  index: int, reference to holds detected index ...
 * @param  bar: reference to Specified Bar ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( bool )
 */
bool HasActed(
    int &index,
    XOHCL &bar,
    XBoxZone &source[] //
)
{
    //
    XBoxZone selected[];
    int count = ExtractActeds(bar, selected, source);
    bool result = IsValidSize(count);
    if (result)
    {
        //
        index = GetYoungest(selected);
        if (IsValidIndex(index))
        {
            result = FindIndex(index, selected[index], source);
        }
    }

    //
    XClean(selected);

    //
    return result;
}
// #endregion

// #region Zone Extractors ...
/**
 * Extract Affected Zones for Specified Bar ...
 *
 * @param  bar: XOHCL, reference to Specified Bar ...
 * @param  source: XBoxZone, reference collection to Provide Data Source ...
 * @param  dest: XBoxZone, reference collection to holds result ...
 * @param  forDir: ENUM_X_DIRECTION, Specified Zones Direction to Lookup ...
 *
 * @return ( int )
 */
int ExtractAffectedZones(
    XOHCL &bar,
    XBoxZone &source[],
    XBoxZone &dest[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    // Validate ...
    bool has = bar.IsValid() &&
               HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    bool isActed;
    bool isTested;
    bool isInside;
    bool isFakeBreaked;
    for (int i = 0; i < ArraySize(source); i++)
    {
        //
        // Validate Direction ...
        has =
            !HasDirection(forDir) ||
            forDir == source[i].dir;
        if (!has)
        {
            continue;
        }

        //
        // Check Affected ...
        isActed = IsActed(bar, source[i]);
        isTested = IsTested(bar, source[i]);
        isInside = IsInside(bar, source[i]);
        isFakeBreaked = IsFakeBreaked(bar, source[i]);

        //
        has =
            isActed ||
            isTested ||
            isInside ||
            isFakeBreaked;
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all Items which Specified Bar is inside them ...
 *
 * @param  bar: reference to Specified Bar ...
 * @param  dest: reference to result ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( int )
 */
int ExtractInsides(
    XOHCL &bar,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    bool has = bar.IsValid() &&
               HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = IsInside(
            bar,
            source[i] //
        );
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all Items which Specified Bar is Acted them ...
 *
 * @param  bar: reference to Specified Bar ...
 * @param  dest: reference to result ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( int )
 */
int ExtractActeds(
    XOHCL &bar,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    // Validate ...
    bool has = bar.IsValid() &&
               HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = IsActed(
            bar,
            source[i] //
        );
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all Items which Specified Bar is Breaked them ...
 *
 * @param  bar: reference to Specified Bar ...
 * @param  dest: reference to result ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( int )
 */
int ExtractBreakeds(
    XOHCL &bar,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    bool has = bar.IsValid() &&
               HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = IsBreaked(
            bar,
            source[i] //
        );
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Extract all Items which Specified Bar is Rejected them ...
 *
 * @param  bar: reference to Specified Bar ...
 * @param  dest: reference to result ...
 * @param  source: reference collection to provide source ...
 *
 * @return ( int )
 */
int ExtractRejecteds(
    XOHCL &bar,
    XBoxZone &dest[],
    XBoxZone &source[] //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Validate ...
    bool has = bar.IsValid() &&
               HasChild(source);
    if (!has)
    {
        return result;
    }

    //
    int count = ArraySize(source);
    for (int i = 0; i < count; i++)
    {
        //
        has = IsRejected(
            bar,
            source[i] //
        );
        if (has)
        {
            //
            AddIfNotExists(
                source[i],
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Collect all Bars which Breakes Specified Zone ...
 *
 * @param  zone: reference to Specified Zone ...
 * @param  dest: reference Collection to hold result ...
 *
 * @return ( int )
 */
int GetBreakerBars(
    XBoxZone &zone,
    XOHCL &dest[] //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = zone.IsValid();
    if (!has)
    {
        return result;
    }

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Do Action ...
    XOHCL iBar;
    int start = zone.ToIndex();
    int end = zone.FromIndex();
    for (int i = start; i < end; i++)
    {
        //
        // Initialize indexed Bar ...
        has = iBar.Init(
            zone.symbol,
            zone.period,
            i //
        );
        if (!has)
        {
            continue;
        }

        //
        has = IsBreaked(
            iBar,
            zone,
            false // Breakes by Close ...
        );
        if (has)
        {
            //
            AddIfNotExists(
                iBar,
                dest //
            );
        }

        //
        iBar.Clean();
    }

    //
    result = ArraySize(dest);

    //
    iBar.Clean();

    //
    return result;
}

/**
 * Collect all Bars which Inside Specified Zone ...
 *
 * @param  zone: reference to Specified Zone ...
 * @param  dest: reference Collection to hold result ...
 *
 * @return ( int )
 */
int GetInsideBars(
    XBoxZone &zone,
    XOHCL &dest[] //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = zone.IsValid();
    if (!has)
    {
        return result;
    }

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Do Action ...
    XOHCL iBar;
    int start = zone.ToIndex();
    int end = zone.FromIndex();
    for (int i = start; i < end; i++)
    {
        //
        // Initialize indexed Bar ...
        has = iBar.Init(
            zone.symbol,
            zone.period,
            i //
        );
        if (!has)
        {
            continue;
        }

        //
        has = IsInside(
            iBar,
            zone //
        );
        if (has)
        {
            //
            AddIfNotExists(
                iBar,
                dest //
            );
        }

        //
        iBar.Clean();
    }

    //
    result = ArraySize(dest);

    //
    iBar.Clean();

    //
    return result;
}

/**
 * Collect all Bars which Tested Specified Zone ...
 *
 * @param  zone: reference to Specified Zone ...
 * @param  dest: reference Collection to hold result ...
 *
 * @return ( int )
 */
int GetTesterBars(
    XBoxZone &zone,
    XOHCL &dest[] //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = zone.IsValid();
    if (!has)
    {
        return result;
    }

    //
    // Prepare ...
    SpecifiedClean(dest);

    //
    // Do Action ...
    XOHCL iBar;
    int start = zone.ToIndex();
    int end = zone.FromIndex();
    for (int i = start; i < end; i++)
    {
        //
        // Initialize indexed Bar ...
        has = iBar.Init(
            zone.symbol,
            zone.period,
            i //
        );
        if (!has)
        {
            continue;
        }

        //
        has = IsTested(
            iBar,
            zone //
        );
        if (has)
        {
            //
            AddIfNotExists(
                iBar,
                dest //
            );
        }

        //
        iBar.Clean();
    }

    //
    result = ArraySize(dest);

    //
    iBar.Clean();

    //
    return result;
}

/**
 * Validate Zones Range ...
 *
 * @param  source: XBoxZone, collection reference to Validate ...
 * @param  minAllowedRange: double, min allowed range to Validate ...
 *
 * @return ( int )
 */
int ValidateZonesRange(
    XBoxZone &source[],
    double minAllowedRange //
)
{
    //
    int result = 0;

    //
    // Validate ...
    bool has = HasChild(source) &&
               minAllowedRange > 0;
    if (!has)
    {
        return result;
    }

    //
    XBoxZone iTMP;
    XBoxZone tmps[];
    Copy(source, tmps);
    XClean(source);
    while (HasChild(tmps))
    {
        //
        iTMP.Clean();
        iTMP = tmps[0];
        ArrayRemove(tmps, 0, 1);

        //
        // Check Range Validation ...
        has = iTMP.GetRange() > minAllowedRange;
        if (has)
        {
            AddIfNotExists(iTMP, source);
        }
    }

    //
    result = ArraySize(source);

    //
    // Cleanup ...
    XClean(tmps);
    iTMP.Clean();

    //
    return result;
}

/**
 * Remove Breaked Zones ...
 *
 * @param  source: XBoxZone, collection reference to Remove Breaked Zones ...
 * @param  priceType: ENUM_X_PRICE, Specified Price type of Break Detection ...
 *
 * @return ( int )
 */
int RemoveBreakedZones(
    XBoxZone &source[],
    ENUM_X_PRICE priceType = X_PRICE_CLOSE //
)
{
    //
    int result = 0;

    //
    // Validate ...
    if (!HasChild(source) || !IsXValid(priceType))
    {
        return result;
    }

    //
    XBoxZone tmp[];
    Copy(source, tmp);
    XClean(source);
    XBoxZone iTMP;
    bool isBreaked = false;
    while (HasChild(tmp))
    {
        //
        iTMP.Clean();
        iTMP = tmp[0];
        ArrayRemove(tmp, 0, 1);

        //
        isBreaked = IsBreaked(iTMP, priceType);
        if (!isBreaked)
        {
            //
            AddIfNotExists(iTMP, source);
        }
    }

    //
    result = ArraySize(source);

    //
    // Cleanup ...
    XClean(tmp);
    iTMP.Clean();

    //
    return result;
}
// #endregion
//+------------------------------------------------------------------+

// #region Bar Extensions ...
/**
 * Check Specified Bar is Breaked or not ...
 *
 * @param  bar: XOHCL, Specified Bar for Checking Proce ...
 * @param  breakerBar: XOHCL, Hold's Breaker Bar if Exists ...
 * @param  barIndex: int, Start Checking Bar till Bar's Index ...
 * @param  breakDir: ENUM_X_DIRECTION, Checking Direction Type, if Bullish Break Price must bigger, otherwise must lower ...
 * @param  barPriceType: ENUM_X_PRICE, Price type to Check Break ...
 * @param  breakPriceType: ENUM_X_PRICE, Price Type to Break Bar's Price ...
 *
 * @return ( bool )
 */
bool IsBarBreaked(
    XOHCL &bar,
    XOHCL &breakerBar,
    int barIndex = 0,
    ENUM_X_DIRECTION breakDir = X_DIRECTION_NONE,
    ENUM_X_PRICE barPriceType = X_PRICE_CLOSE,
    ENUM_X_PRICE breakPriceType = X_PRICE_CLOSE //
)
{
    //
    bool result = false;

    //
    // Normalize ...
    breakerBar.Clean();
    if (barIndex < 0)
    {
        barIndex = 0;
    }

    //
    // Validate ...
    result =
        bar.IsValid() &&
        HasDirection(breakDir) &&
        IsXValid(barPriceType) &&
        IsXValid(breakPriceType) &&
        barIndex < bar.Index();
    if (!result)
    {
        return result;
    }

    //
    // Loop for Validating ...
    XOHCL tmpBar;
    bool has = false;
    double tmpPrice = EMPTY_VALUE;
    double barPrice = bar.GetPrice(barPriceType);
    for (int i = barIndex; i < bar.Index(); i++)
    {
        //
        has = tmpBar.Init(bar.symbol, bar.period, i);
        if (!has)
        {
            continue;
        }

        //
        tmpPrice = tmpBar.GetPrice(breakPriceType);
        has = NotEmptyZero(tmpPrice);
        if (!has)
        {
            //
            tmpBar.Clean();

            //
            continue;
        }

        //
        has =
            IsXBullish(breakDir)
                ? tmpPrice > barPrice
                : tmpPrice < barPrice;
        if (has)
        {
            //
            breakerBar = tmpBar;
            tmpBar.Clean();
            break;
        }

        //
        tmpBar.Clean();
    }

    //
    result = breakerBar.IsValid();

    //
    return result;
}
// #endregion

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-poi.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Seriallize Library
// ---------------------------------------
// Name: XPOILib
// Description: All models related to POIs ...
//
//
// 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

//
// Imports ...
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Libraries/x-saherelm.common.lib.mq5"
#include <Arrays/ArrayObj.mqh>

//
// Definitions ...

/**
 * All Available Market Patterns ...
 **/
enum ENUM_X_BAR_PATTERN
{
    X_BAR_PATTERN_NONE = 0,
    X_BAR_PATTERN_HIGH = 1,
    X_BAR_PATTERN_LOW = 2,
    X_BAR_PATTERN_MOMENTUM = 3,
    X_BAR_PATTERN_ENGULFED = 4,
    X_BAR_PATTERN_TRUE_GAPED = 5,
    X_BAR_PATTERN_PINNED = 6,
    X_BAR_PATTERN_STAR = 7,
    X_BAR_PATTERN_PIERCING = 8,
    X_BAR_PATTERN_RISING = 9,
    X_BAR_PATTERN_FLAG = 10,
    X_BAR_PATTERN_SIGNALKEY = 11,
    X_BAR_PATTERN_OB = 12,
    X_BAR_PATTERN_FVG = 13,
    X_BAR_PATTERN_SUPPORT = 14,
    X_BAR_PATTERN_RESISTANCE = 15,
    X_BAR_PATTERN_PULLBACK = 16,
    X_BAR_PATTERN_CONSOLIDATION = 17,
    X_BAR_PATTERN_LEG = 18,
};

/**
 * Validate an ENUM member ...
 *
 * @param  value: ENUM_X_BAR_PATTERN ...
 *
 * @return ( bool )
 */
bool IsXValid(ENUM_X_BAR_PATTERN value)
{
    return value != X_BAR_PATTERN_NONE;
}

/**
 * Converts a Pattern ENUM to String Representation ...
 *
 * @param  value: ENUM_X_BAR_PATTERN, member ...
 *
 * @return ( string )
 */
string ToXString(ENUM_X_BAR_PATTERN value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_BAR_PATTERN_NONE:
        result = "X_NONE";
        break;

    //
    case X_BAR_PATTERN_HIGH:
        result = "XHIGHP";
        break;

    //
    case X_BAR_PATTERN_LOW:
        result = "XLOWP";
        break;

    //
    case X_BAR_PATTERN_MOMENTUM:
        result = "XMOMENTUMP";
        break;

    //
    case X_BAR_PATTERN_ENGULFED:
        result = "XENGULFEDP";
        break;

    //
    case X_BAR_PATTERN_TRUE_GAPED:
        result = "XGAPEDP";
        break;

    //
    case X_BAR_PATTERN_PINNED:
        result = "XPINNEDP";
        break;

    //
    case X_BAR_PATTERN_STAR:
        result = "XSTARP";
        break;

    //
    case X_BAR_PATTERN_PIERCING:
        result = "XPIERCINGP";
        break;

    //
    case X_BAR_PATTERN_RISING:
        result = "XRISINGP";
        break;

    //
    case X_BAR_PATTERN_FLAG:
        result = "XFLAGP";
        break;

    //
    case X_BAR_PATTERN_SIGNALKEY:
        result = "XSIGNALKEYP";
        break;

    //
    case X_BAR_PATTERN_OB:
        result = "XOBP";
        break;

    //
    case X_BAR_PATTERN_FVG:
        result = "XFVGP";
        break;

    //
    case X_BAR_PATTERN_SUPPORT:
        result = "XSUPPORTP";
        break;

    //
    case X_BAR_PATTERN_RESISTANCE:
        result = "XRESISTANCEP";
        break;

    //
    case X_BAR_PATTERN_PULLBACK:
        result = "XPULLBACKP";
        break;

    //
    case X_BAR_PATTERN_CONSOLIDATION:
        result = "XCONSOLIDATIONP";
        break;

    //
    case X_BAR_PATTERN_LEG:
        result = "XLEGP";
        break;
    }

    //
    return result;
}

/**
 * Parse an String Representation to it's Corresponding Bar Pattern ENUM ...
 *
 * @param  value: string, provided pattern's string ...
 *
 * @return ( ENUM_X_BAR_PATTERN )
 */
ENUM_X_BAR_PATTERN ToBarPattern(string value)
{
    //
    ENUM_X_BAR_PATTERN result = X_BAR_PATTERN_NONE;

    //
    // Validate ...
    if (!IsXValid(value))
    {
        return result;
    }

    //
    if (value == ToXString(X_BAR_PATTERN_NONE))
    {
        result = X_BAR_PATTERN_NONE;
    }
    else if (value == ToXString(X_BAR_PATTERN_HIGH))
    {
        result = X_BAR_PATTERN_HIGH;
    }
    else if (value == ToXString(X_BAR_PATTERN_LOW))
    {
        result = X_BAR_PATTERN_LOW;
    }
    else if (value == ToXString(X_BAR_PATTERN_MOMENTUM))
    {
        result = X_BAR_PATTERN_MOMENTUM;
    }
    else if (value == ToXString(X_BAR_PATTERN_ENGULFED))
    {
        result = X_BAR_PATTERN_ENGULFED;
    }
    else if (value == ToXString(X_BAR_PATTERN_TRUE_GAPED))
    {
        result = X_BAR_PATTERN_TRUE_GAPED;
    }
    else if (value == ToXString(X_BAR_PATTERN_PINNED))
    {
        result = X_BAR_PATTERN_PINNED;
    }
    else if (value == ToXString(X_BAR_PATTERN_STAR))
    {
        result = X_BAR_PATTERN_STAR;
    }
    else if (value == ToXString(X_BAR_PATTERN_PIERCING))
    {
        result = X_BAR_PATTERN_PIERCING;
    }
    else if (value == ToXString(X_BAR_PATTERN_RISING))
    {
        result = X_BAR_PATTERN_RISING;
    }
    else if (value == ToXString(X_BAR_PATTERN_FLAG))
    {
        result = X_BAR_PATTERN_FLAG;
    }
    else if (value == ToXString(X_BAR_PATTERN_SIGNALKEY))
    {
        result = X_BAR_PATTERN_SIGNALKEY;
    }
    else if (value == ToXString(X_BAR_PATTERN_OB))
    {
        result = X_BAR_PATTERN_OB;
    }
    else if (value == ToXString(X_BAR_PATTERN_FVG))
    {
        result = X_BAR_PATTERN_FVG;
    }
    else if (value == ToXString(X_BAR_PATTERN_SUPPORT))
    {
        result = X_BAR_PATTERN_SUPPORT;
    }
    else if (value == ToXString(X_BAR_PATTERN_RESISTANCE))
    {
        result = X_BAR_PATTERN_RESISTANCE;
    }
    else if (value == ToXString(X_BAR_PATTERN_PULLBACK))
    {
        result = X_BAR_PATTERN_PULLBACK;
    }
    else if (value == ToXString(X_BAR_PATTERN_CONSOLIDATION))
    {
        result = X_BAR_PATTERN_CONSOLIDATION;
    }
    else if (value == ToXString(X_BAR_PATTERN_LEG))
    {
        result = X_BAR_PATTERN_LEG;
    }

    //
    return result;
}

/**
 * Get All Available Valid Bar Patterns ...
 *
 * @param  dest: ENUM_X_BAR_PATTERN, reference collection to fill ...
 *
 * @return ( int )
 */
int GetAllBarPatters(ENUM_X_BAR_PATTERN &dest[])
{
    //
    XClean(dest);

    //
    Add(X_BAR_PATTERN_HIGH, dest);
    Add(X_BAR_PATTERN_LOW, dest);
    Add(X_BAR_PATTERN_MOMENTUM, dest);
    Add(X_BAR_PATTERN_ENGULFED, dest);
    Add(X_BAR_PATTERN_TRUE_GAPED, dest);
    Add(X_BAR_PATTERN_PINNED, dest);
    Add(X_BAR_PATTERN_STAR, dest);
    Add(X_BAR_PATTERN_PIERCING, dest);
    Add(X_BAR_PATTERN_RISING, dest);
    Add(X_BAR_PATTERN_FLAG, dest);
    Add(X_BAR_PATTERN_SIGNALKEY, dest);
    Add(X_BAR_PATTERN_OB, dest);
    Add(X_BAR_PATTERN_FVG, dest);
    Add(X_BAR_PATTERN_SUPPORT, dest);
    Add(X_BAR_PATTERN_RESISTANCE, dest);
    Add(X_BAR_PATTERN_PULLBACK, dest);
    Add(X_BAR_PATTERN_CONSOLIDATION, dest);
    Add(X_BAR_PATTERN_LEG, dest);

    //
    return ArraySize(dest);
}

/**
 * Get All Available Valid Bar Patterns ...
 *
 * @param  dest: ENUM_X_BAR_PATTERN, collection reference to Fill Patterns ...
 * @param  dir: ENUM_X_DIRECTION, collection reference to Fill Directions ...
 * @param  forDir: ENUM_X_DIRECTION, Force Patterns Direction ...
 *
 * @return ( int )
 */
int GetAllBarPatters(
    ENUM_X_BAR_PATTERN &dest[],
    ENUM_X_DIRECTION &dir[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    XClean(dest);
    XClean(dir);

    //
    Add(X_BAR_PATTERN_HIGH, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_LOW, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_MOMENTUM, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_ENGULFED, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_TRUE_GAPED, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_PINNED, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_STAR, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_PIERCING, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_RISING, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_FLAG, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_SIGNALKEY, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_OB, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_FVG, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_SUPPORT, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_RESISTANCE, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_PULLBACK, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_CONSOLIDATION, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_LEG, dest);
    Add(forDir, dir);

    //
    return ArraySize(dest);
}

/**
 * Fill Required Candlestick Patters ...
 *
 * @param  dest: ENUM_X_BAR_PATTERN, collection reference to Fill ...
 *
 * @return ( int )
 */
int GetCandlestickPatterns(ENUM_X_BAR_PATTERN &dest[])
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);

    //
    Add(X_BAR_PATTERN_STAR, dest);
    Add(X_BAR_PATTERN_FLAG, dest);
    Add(X_BAR_PATTERN_PINNED, dest);
    Add(X_BAR_PATTERN_RISING, dest);
    Add(X_BAR_PATTERN_MOMENTUM, dest);
    Add(X_BAR_PATTERN_ENGULFED, dest);
    Add(X_BAR_PATTERN_PIERCING, dest);
    Add(X_BAR_PATTERN_SIGNALKEY, dest);
    Add(X_BAR_PATTERN_TRUE_GAPED, dest);

    //
    result = ArraySize(dest);

    //
    return result;
}

/**
 * Fill Required Candlestick Patters ...
 *
 * @param  dest: ENUM_X_BAR_PATTERN, collection reference to Fill Patterns ...
 * @param  dir: ENUM_X_DIRECTION, collection reference to Fill Directions ...
 * @param  forDir: ENUM_X_DIRECTION, Force Patterns Direction ...
 *
 * @return ( int )
 */
int GetCandlestickPatterns(
    ENUM_X_BAR_PATTERN &dest[],
    ENUM_X_DIRECTION &dir[],
    ENUM_X_DIRECTION forDir = X_DIRECTION_NONE //
)
{
    //
    int result = 0;

    //
    // Prepare ...
    XClean(dest);
    XClean(dir);

    //
    Add(X_BAR_PATTERN_STAR, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_FLAG, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_PINNED, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_RISING, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_MOMENTUM, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_ENGULFED, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_PIERCING, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_SIGNALKEY, dest);
    Add(forDir, dir);

    //
    Add(X_BAR_PATTERN_TRUE_GAPED, dest);
    Add(forDir, dir);

    //
    result = ArraySize(dest);

    //
    return result;
}

//
// Structures ...

/**
 * Model a Trend Line ...
 **/
struct XTrend
{
    //
    string symbol;
    datetime time;
    ENUM_TIMEFRAMES period;

    //
    double fromValue;
    datetime fromTime;

    //
    double toValue;
    datetime toTime;

    //
    ENUM_X_DIRECTION dir;

    //
    // Constructor ...
    XTrend()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Initialize ...
     *
     * @param  _symbol: string, Specified Symbol ...
     * @param  _period: ENUM_TIMEFRAMES, Specified Period ...
     * @param  _time: datetime, Detection Time ...
     * @param  _fromValue: double, From Value ...
     * @param  _fromTime: datetime, From Time ...
     * @param  _toValue: double, To Value ...
     * @param  _toTime: datetime, To Time ...
     *
     * @return ( bool )
     */
    bool Init(
        string _symbol,
        ENUM_TIMEFRAMES _period,
        datetime _time,
        double _fromValue,
        datetime _fromTime,
        double _toValue,
        datetime _toTime //
    )
    {
        //
        bool result = false;

        //
        time = _time;
        symbol = _symbol;
        period = _period;
        toTime = _toTime;
        toValue = _toValue;
        fromTime = _fromTime;
        fromValue = _fromValue;

        //
        result = IsValid();
        if (!result)
        {
            Clean();
        }

        //
        // Setting Direction ...
        if (result)
        {
            //
            dir =
                fromValue < toValue
                    ? X_DIRECTION_BULLISH
                : fromValue > toValue
                    ? X_DIRECTION_BEARISH
                    : X_DIRECTION_NONE;
        }

        //
        return result;
    }

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        time = NULL;
        toValue = 0;
        fromValue = 0;
        symbol = NULL;
        period = NULL;
        toTime = NULL;
        fromTime = NULL;
        dir = X_DIRECTION_NONE;

        //
        ZeroMemory(this);
    }

    /**
     * Validate ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            IsXValid(time) &&
            IsXValid(symbol) &&
            IsXValid(period) &&
            IsXValid(toTime) &&
            IsXValid(fromTime) &&
            NotEmptyZero(toValue) &&
            NotEmptyZero(fromValue) &&
            fromTime < toTime;

        //
        return result;
    }

    /**
     * Check Trend Has Specific Direction ...
     *
     * @return ( bool )
     */
    bool HasTrend()
    {
        return IsValid() &&
               HasDirection(dir);
    }

    /**
     * Check Trend is Bullish ...
     *
     * @return ( bool )
     */
    bool IsBullish()
    {
        return HasTrend() &&
               IsXBullish(dir);
    }

    /**
     * Check Trend is Bearish ...
     *
     * @return ( bool )
     */
    bool IsBearish()
    {
        return HasTrend() &&
               IsXBearish(dir);
    }

    /**
     * Get Unique Identifier ...
     *
     * @return ( string )
     */
    string GetObjectID()
    {
        //
        string result = NULL;

        //
        if (IsValid())
        {
            double ifv = NormalizePrice(fromValue, symbol);
            result = "XTRND_" + ToXString(dir) + "_" + ToXString(ifv) + "_" + ToXString(TimeToSeconds(fromTime));
        }

        //
        return result;
    }
};

/**
 * Represent a Pivot ...
 **/
struct XPivot
{
    //
    // Props ...
    double value;           // Value
    datetime time;          // Time
    string symbol;          // Symbol
    ENUM_X_DIRECTION dir;   // Direction
    ENUM_TIMEFRAMES period; // Period
    ENUM_X_PIVOT_TYPE type; // Type

    //
    string prefix; // Object Prefix

    //
    // Constructor ...
    XPivot()
    {
        Clean();
    }

    //
    // Init ...

    /**
     * Initialize Structure ...
     *
     * @param  _value: double, value of point ...
     * @param  _time: datetime, time of point ...
     * @param  _symbol: string, Symbol ...
     * @param  _dir: ENUM_X_DIRECTION, direction of point ...
     * @param  _period: ENUM_TIMEFRAMES, period ...
     * @param  _type: ENUM_X_PIVOT_TYPE, point type ...
     *
     * @return ( bool )
     */
    bool Init(
        double _value,
        datetime _time,
        string _symbol,
        ENUM_X_DIRECTION _dir,
        ENUM_TIMEFRAMES _period,
        ENUM_X_PIVOT_TYPE _type //
    )
    {
        //
        bool result = false;

        //
        dir = _dir;
        time = _time;
        type = _type;
        value = _value;
        symbol = _symbol;
        period = _period;

        //
        result = IsValid();
        if (!result)
        {
            Clean();
        }

        //
        return result;
    }

    //
    // Tools ...

    /**
     * Cleanup Model ...
     */
    void Clean()
    {
        //
        value = 0;
        time = NULL;
        symbol = NULL;
        period = NULL;
        prefix = NULL;
        dir = X_DIRECTION_NONE;
        type = X_PIVOT_TYPE_NONE;

        //
        ZeroMemory(this);
    }

    /**
     * Validate Model ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            value > 0 &&
            IsXValid(type) &&
            HasDirection(dir) &&
            IsSpecifiedValid(time) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period);

        //
        return result;
    }

    /**
     * Check Model is Peak or not ...
     *
     * @return ( bool )
     */
    bool IsPeak()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            IsXPeak(type);

        //
        return result;
    }

    /**
     * Check Model is Vale or not ...
     *
     * @return ( bool )
     */
    bool IsVale()
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            IsXVale(type);

        //
        return result;
    }

    /**
     * Check Point is Bullish or not ...
     *
     * @return ( bool )
     */
    bool IsBullish()
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 IsSpecifiedBullish(dir);

        //
        return result;
    }

    /**
     * Check Point is Bearish or not ...
     *
     * @return ( bool )
     */
    bool IsBearish()
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 IsSpecifiedBearish(dir);

        //
        return result;
    }

    /**
     * Check two Struct are Same or not ...
     *
     * @param  item: XPivot ...
     *
     * @return ( bool )
     */
    bool IsSameAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            dir == item.dir &&
            type == item.type &&
            time == item.time &&
            value == item.value &&
            symbol == item.symbol &&
            period == item.period;

        //
        if (result && IsXValid(prefix))
        {
            //
            result =
                result &&
                prefix == item.prefix;
        }

        //
        return result;
    }

    /**
     * Calculate Pivot Age ...
     *
     * @return ( int )
     */
    int GetAge()
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetBarIndex(
            symbol,
            period,
            time //
        );

        //
        return result;
    }

    /**
     * Get Pivot Time Bar ...
     *
     * @param  bar: XOHCL, reference to hold result ...
     *
     * @return ( bool )
     */
    bool GetBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        // Prepare ...
        bar.Clean();

        //
        // Validate ...
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Initial Bar ...
        result = bar.Init(
            symbol,
            period,
            time //
        );

        //
        if (!result)
        {
            bar.Clean();
        }

        //
        return result;
    }

    /**
     * Check to model Has Same Symbol ...
     *
     * @param  item: XPivot, dest model for Checking ...
     *
     * @return ( bool )
     */
    bool IsSameSymbolAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            symbol == item.symbol;

        //
        return result;
    }

    /**
     * Check to model Has Same Period ...
     *
     * @param  item: XPivot, dest model for Checking ...
     *
     * @return ( bool )
     */
    bool IsSamePeriodAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            period == item.period;

        //
        return result;
    }

    /**
     * Check to model Has Same Symbol/Period ...
     *
     * @param  item: XPivot, dest model for Checking ...
     *
     * @return ( bool )
     */
    bool IsSameMarketAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            IsSameSymbolAs(item) &&
            IsSamePeriodAs(item);

        //
        return result;
    }

    /**
     * Check to model Has Same Type ...
     *
     * @param  item: XPivot, dest model for Checking ...
     *
     * @return ( bool )
     */
    bool IsSameTypeAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            type == item.type;

        //
        return result;
    }

    /**
     * Check to model Has Same Direction ...
     *
     * @param  item: XPivot, dest model for Checking ...
     *
     * @return ( bool )
     */
    bool IsSameDirectionAs(XPivot &item)
    {
        //
        bool result = false;

        //
        result =
            IsValid() &&
            item.IsValid() &&
            dir == item.dir;

        //
        return result;
    }

    //
    // Logging Tools ...

    /**
     * Extract Specified Token of Struct ...
     *
     * @return ( string )
     */
    string GetTag()
    {
        //
        string result = NULL;

        //
        string token = GetToken(this);

        //
        result =
            (IsSpecifiedValid(prefix)
                 ? prefix + "_"
                 : "") +
            token;

        //
        return result;
    }

    /**
     * Represent an String Unique Identifier for Chart Objects ...
     *
     * @return ( string )
     */
    string GetObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetTag() + "_" + symbol + "_" + ToXString(period) + "_" + ToXString(dir) + "_" + ToXString(type) + "_" + ToXString(TimeToSeconds(time));

        //
        return result;
    }

    /**
     * Represent a Point as String ...
     *
     * @param  spacer: string, How to Represent Spaces ...
     *
     * @return ( string )
     */
    string GetMessage(string spacer = NULL)
    {
        //
        string result = "";

        //
        // Normalize Spacer ...
        if (!IsSpecifiedValid(spacer))
        {
            spacer = " ";
        }

        //
        // Validate ...
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            ToXString(dir) + spacer + GetTag() + ":" + spacer +
            "Symbol: " + symbol + spacer +
            "Period: " + ToXString(period) + spacer +
            "Type: " + ToXString(type) + spacer +
            "Direction: " + ToXString(dir) + spacer +
            "Value: " + ToXString(value) + spacer +
            "Time: " + ToXString(time) + spacer;

        //
        return result;
    }
};

/**
 * Model a Box ...
 **/
struct XBoxZone
{
    //
    // Props ...
    string symbol;
    ENUM_TIMEFRAMES period;

    //
    double upper;
    double lower;

    //
    datetime at;
    datetime from;
    datetime to;

    //
    string type;
    ENUM_X_DIRECTION dir;

    //
    // Special Props ...
    datetime breakAt;
    datetime tests[];
    datetime acteds[];
    datetime breaks[];
    datetime insides[];
    datetime refines[];
    datetime fakeBreaks[];

    //
    // Constructor ...
    XBoxZone()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup Model ...
     */
    void Clean()
    {
        //
        upper = 0;
        lower = 0;
        to = NULL;
        at = NULL;
        from = NULL;
        symbol = NULL;
        period = NULL;
        dir = X_DIRECTION_NONE;

        //
        SpecialClean();

        //
        ZeroMemory(this);
    }

    bool Init(
        string _symbol,
        ENUM_TIMEFRAMES _period,
        double _upper,
        double _lower,
        datetime _from,
        datetime _to,
        ENUM_X_DIRECTION _dir,
        string _type = "",
        datetime _at = NULL //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result = IsXValid(_symbol) &&
                 IsXValid(_period) &&
                 _upper > 0 &&
                 _lower > 0 &&
                 _upper > _lower &&
                 IsXValid(_from) &&
                 IsXValid(_to) &&
                 _from < _to &&
                 HasDirection(_dir);
        if (!result)
        {
            return result;
        }

        //
        this.to = _to;
        this.at = _at;
        this.dir = _dir;
        this.from = _from;
        this.type = _type;
        this.lower = _lower;
        this.upper = _upper;
        this.symbol = _symbol;
        this.period = _period;

        //
        result = IsValid();
        if (!result)
        {
            Clean();
        }

        //
        return result;
    }

    /**
     * Clean Special Props ...
     */
    void SpecialClean()
    {
        //
        breakAt = NULL;
        XClean(tests);
        XClean(acteds);
        XClean(breaks);
        XClean(insides);
        XClean(refines);
        XClean(fakeBreaks);
    }

    /**
     * Validate Model ...
     *
     * @return ( bool )
     */
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            upper > 0 &&
            lower > 0 &&
            from < to &&
            IsSpecifiedValid(to) &&
            upper >= lower &&
            IsSpecifiedValid(from) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period)
            //
            ;

        //
        return result;
    }

    /**
     * Check Box is Bullish or not ...
     *
     * @return ( bool )
     */
    bool IsBullish()
    {
        return IsValid() &&
               IsSpecifiedBullish(dir);
    }

    /**
     * Check Box is Bearish or not ...
     *
     * @return ( bool )
     */
    bool IsBearish()
    {
        return IsValid() &&
               IsSpecifiedBearish(dir);
    }

    /**
     * Clone Box by Provided Source ...
     *
     * @return ( bool )
     */
    bool Clone(XBoxZone &source)
    {
        //
        bool result = false;

        //
        Clean();
        result = source.IsValid();
        if (!result)
        {
            return result;
        }

        //
        // Filling Props ...
        to = source.to;
        dir = source.dir;
        from = source.from;
        type = source.type;
        upper = source.upper;
        lower = source.lower;
        symbol = source.symbol;
        period = source.period;

        //
        result = IsValid();
        if (!result)
        {
            Clean();
        }

        //
        return result;
    }

    /**
     * Check Box is Same as Other ...
     *
     * @param  item: XBoxZone instance Resource ...
     *
     * @return ( bool )
     */
    bool IsSameAs(XBoxZone &item)
    {
        //
        return IsValid() &&
               item.IsValid() &&
               dir == item.dir &&
               from == item.from &&
               type == item.type &&
               lower == item.lower &&
               upper == item.upper &&
               symbol == item.symbol &&
               period == item.period;
    }

    /**
     * Retrieve Box Age ...
     *
     * @param  forPeriod: ENUM_TIMEFRAMES member ...
     *
     * @return ( int )
     */
    int GetAge(
        ENUM_TIMEFRAMES forPeriod = NULL //
    )
    {
        //
        int result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (!IsSpecifiedValid(forPeriod))
        {
            forPeriod = period;
        }

        //
        result = iBarShift(
            symbol,
            forPeriod,
            from //
        );

        //
        return result;
    }

    /**
     * Calculate Box Middle Price ...
     *
     * @return ( double )
     */
    double GetMid()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        double diffAvg = (upper - lower) / 2;
        result = lower + diffAvg;
        result = NormalizePrice(result, symbol);

        //
        return result;
    }

    /**
     * Calculate Box Leg N Price ...
     * N default is 2
     *
     * @param  leg: int ...
     *
     * @return ( double )
     */
    double GetLeg(int leg = 2)
    {
        //
        double result = 0;

        //
        leg = NormalizeInt(leg, 2);

        //
        if (!IsValid())
        {
            return result;
        }

        //
        double range = (upper - lower) * leg;
        result =
            IsBullish()
                ? upper + range
                : lower - range;
        result = NormalizePrice(result, symbol);

        //
        return result;
    }

    /**
     * Calculate Box Range ...
     *
     * @return ( double )
     */
    double GetRange()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = (upper - lower);
        result = NormalizePrice(result, symbol);

        //
        return result;
    }

    /**
     * Retrieve Box Applied Price ...
     *
     * @return ( double )
     */
    double GetDirectionalPrice()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            IsBullish()
                ? upper
                : lower;

        //
        return result;
    }

    /**
     * Retrieve Box Breaked Price ...
     *
     * @return ( double )
     */
    double GetInDirectionalPrice()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            IsBullish()
                ? lower
                : upper;

        //
        return result;
    }

    /**
     * To Bar Index ...
     *
     * @return ( int )
     */
    int ToIndex(
        ENUM_TIMEFRAMES forPeriod = NULL //
    )
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (!IsSpecifiedValid(forPeriod))
        {
            forPeriod = period;
        }

        //
        result = GetBarIndex(
            symbol,
            forPeriod,
            to //
        );

        //
        return result;
    }

    /**
     * Retrieve To Bar ...
     *
     * @return ( bool )
     */
    bool ToBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        int idx = ToIndex();

        //
        result =
            IsValid() &&
            IsValidIndex(idx);
        if (!result)
        {
            return result;
        }

        //
        result = bar.Init(
            symbol,
            period,
            idx //
        );

        //
        return result;
    }

    /**
     * At Bar Index ...
     *
     * @return ( int )
     */
    int AtIndex(
        ENUM_TIMEFRAMES forPeriod = NULL //
    )
    {
        //
        int result = -1;

        //
        if (!IsValid() || !IsXValid(at))
        {
            return result;
        }

        //
        if (!IsSpecifiedValid(forPeriod))
        {
            forPeriod = period;
        }

        //
        result = GetBarIndex(
            symbol,
            forPeriod,
            at //
        );

        //
        return result;
    }

    /**
     * Retrieve At Bar ...
     *
     * @return ( bool )
     */
    bool AtBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        int idx = AtIndex();

        //
        result =
            IsValid() &&
            IsValidIndex(idx);
        if (!result)
        {
            return result;
        }

        //
        result = bar.Init(
            symbol,
            period,
            idx //
        );

        //
        return result;
    }

    /**
     * From Bar Index ...
     *
     * @return ( int )
     */
    int FromIndex(
        ENUM_TIMEFRAMES forPeriod = NULL //
    )
    {
        //
        int result = -1;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (!IsSpecifiedValid(forPeriod))
        {
            forPeriod = period;
        }

        //
        result = GetBarIndex(
            symbol,
            forPeriod,
            from //
        );

        //
        return result;
    }

    /**
     * Retrieve From Bar ...
     *
     * @return ( bool )
     */
    bool FromBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        bar.Clean();

        //
        int idx = FromIndex();

        //
        result =
            IsValid() &&
            IsValidIndex(idx);
        if (!result)
        {
            return result;
        }

        //
        result = bar.Init(
            symbol,
            period,
            idx //
        );

        //
        return result;
    }

    /**
     * Retrieve Unique Identifier ...
     *
     * @return ( string )
     */
    string GetTag()
    {
        //
        int digits = GetDigits(symbol);
        double nUpper = NormalizeDouble(NormalizePrice(upper, symbol), digits);
        double nLower = NormalizeDouble(NormalizePrice(lower, symbol), digits);

        //
        string typeStr = IsSpecifiedValid(type)
                             ? type
                             : "XBOX";
        string result =
            //
            typeStr + "_" +
            ToString(dir) + "_" +
            symbol + "_" +
            ToXString(period) + "_" +
            ToXString(nUpper) + "_" +
            ToXString(nLower)
            //
            ;

        //
        return result;
    }

    /**
     * Retrieve Unique Middle Identifier ...
     *
     * @return ( string )
     */
    string GetMiddleTag()
    {
        //
        int digits = GetDigits(symbol);
        double nMid = NormalizeDouble(NormalizePrice(GetMid(), symbol), digits);

        //
        string typeStr = IsSpecifiedValid(type)
                             ? type
                             : "XBOX";
        string result =
            //
            typeStr + "_" +
            "Mid_" +
            ToString(dir) + "_" +
            symbol + "_" +
            ToXString(period) + "_" +
            ToXString(nMid)
            //
            ;

        //
        return result;
    }
};

/**
 * Model a Direction Shift ...
 **/
struct XDirectionShift
{
    //
    // Props ...
    string type;
    datetime at;
    double after;
    double before;
    string symbol;
    ENUM_TIMEFRAMES period;
    ENUM_X_DIRECTION shiftTo;

    //
    // Constructor ...
    XDirectionShift()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        at = NULL;
        after = 0;
        before = 0;
        type = NULL;
        symbol = NULL;
        period = NULL;
        shiftTo = X_DIRECTION_NONE;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            after > 0 &&
            IsSpecifiedValid(at) &&
            IsSpecifiedValid(type) &&
            after != before &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period) &&
            HasDirection(shiftTo);

        //
        return result;
    }

    //
    bool Init(
        string _type,
        datetime _at,
        double _after,
        double _before,
        string _symbol,
        ENUM_TIMEFRAMES _period,
        ENUM_X_DIRECTION _shiftTo //
    )
    {
        //
        bool result = false;

        //
        // Validate ...
        result =
            IsXValid(_at) &&
            IsXValid(_type) &&
            IsXValid(_symbol) &&
            IsXValid(_period) &&
            NotEmptyZero(_after) &&
            NotEmptyZero(_before) &&
            HasDirection(_shiftTo);
        if (!result)
        {
            return result;
        }

        //
        type = _type;
        at = _at;
        after = _after;
        before = _before;
        symbol = _symbol;
        period = _period;
        shiftTo = _shiftTo;

        //
        result = IsValid();

        //
        return result;
    }

    //
    // Get Bar Index of Direction Shift ...
    int Index()
    {
        //
        int result = -1;

        //
        // Validate ...
        if (!IsValid())
        {
            return result;
        }

        //
        // Retrieve Bar Index ...
        result = GetBarIndex(
            symbol,
            period,
            at //
        );

        //
        return result;
    }

    //
    // Get At Bar ...
    bool AtBar(XOHCL &bar)
    {
        //
        bool result = false;

        //
        // Normalize ...
        bar.Clean();

        //
        // Validate ...
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        int idx = Index();
        if (IsValidIndex(idx))
        {
            //
            result = bar.Init(
                symbol,
                period,
                idx //
            );
        }

        //
        result = bar.IsValid();

        //
        return result;
    }

    //
    // Check Same ...
    bool IsSameAs(XDirectionShift &item)
    {
        //
        bool result = false;

        //
        result = IsValid() &&
                 item.IsValid();
        if (!result)
        {
            return result;
        }

        //
        result =
            //
            at == item.at &&
            type == item.type &&
            after == item.after &&
            before == item.before &&
            symbol == item.symbol &&
            period == item.period &&
            shiftTo == item.shiftTo
            //
            ;

        //
        return result;
    }
};

/**
 * Configure Draws ...
 **/
struct XPOIDrawConfig
{
    //
    // Props ...

    //
    color bullishColor;
    color bearishColor;
    color neuturalColor;

    //
    int arrowsWidth;
    int bullishArrow;
    int bearishArrow;
    int arrowsThreshold;
    ENUM_X_PRICE arrowsValePriceType;
    ENUM_X_PRICE arrowsPeakPriceType;

    //
    int zonesWidth;
    bool zonesFill;
    bool ignoreZonesAt;
    ENUM_LINE_STYLE zonesStyle;

    //
    int trendWidth;
    bool trendRayLeft;
    bool trendRayRight;
    ENUM_LINE_STYLE trendStyle;

    //
    // Constructor ...
    XPOIDrawConfig()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup ...
     */
    void Clean()
    {
        //
        bullishColor = CLR_NONE;
        bearishColor = CLR_NONE;
        neuturalColor = CLR_NONE;

        //
        arrowsWidth = 0;
        bullishArrow = 0;
        bearishArrow = 0;
        arrowsThreshold = 0;
        arrowsValePriceType = X_PRICE_NONE;
        arrowsPeakPriceType = X_PRICE_NONE;

        //
        zonesWidth = 1;
        zonesFill = false;
        ignoreZonesAt = true;
        zonesStyle = STYLE_SOLID;

        //
        trendWidth = 0;
        trendRayLeft = false;
        trendRayRight = false;
        trendStyle = STYLE_DOT;
    }

    /**
     * Default Config ...
     */
    void Default()
    {
        //
        bullishColor = clrAqua;
        bearishColor = clrMagenta;
        neuturalColor = clrGray;

        //
        arrowsWidth = 1;
        bullishArrow = 159;
        bearishArrow = 159;
        arrowsThreshold = 5;
        arrowsValePriceType = X_PRICE_LOW;
        arrowsPeakPriceType = X_PRICE_HIGH;

        //
        zonesWidth = 1;
        zonesFill = true;
        ignoreZonesAt = true;
        zonesStyle = STYLE_SOLID;

        //
        trendWidth = 2;
        trendRayLeft = false;
        trendRayRight = true;
        trendStyle = STYLE_DOT;
    }

    //
};

//

### FILE: C:\Users\SaherElm\AppData\Roaming\MetaQuotes\Terminal\572C4A1F743028C65C8BA0B780030F0D\MQL5\Libraries\x-saherelm.x-trade.lib.mq5

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Seriallize Library
// ---------------------------------------
// Name: XTradeLib
// Description: All models related to Trade ...
//
//
// 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

//
// Imports ...
#include "../Libraries/x-saherelm.common.lib.mq5"
#include "../Libraries/x-saherelm.x-guard.lib.mq5"
#include <Trade/Trade.mqh>

//
// Definitions ...

//
// a List of Used Tokens ...

//
// Common ...
string XIDToken = "ID";
string XTicketToken = "TK";
string XModeToken = "MD";
string XTypeToken = "TY";
string XPeriodToken = "PR";
string XProviderToken = "PRV";
string XProfitToken = "PF";

//
string XTPToken = "TP";
string XSLToken = "SL";
string XTimeToken = "TM";
string XEntryToken = "EN";
string XVolumeToken = "VL";
string XSymbolToken = "SMBL";

//
string XSupportToken = "SP";
string XEQMSupportToken = "EQM";
string XEQPToken = "EQP";

//
string XSLTrailToken = "SLT";

//
string XActionToken = "A";

//
string XDirectionalToken = "D";
string XINDirectionalToken = "IND";

//
string XRecoveryToken = "XRCV";

//
// Available Order Modes ...
enum ENUM_X_ORDER_MODES
{
    X_ORDER_MODE_NOTHING,    // Nothing
    X_ORDER_MODE_MARKET,     // Market Order
    X_ORDER_MODE_STOP,       // Pending Stop Order
    X_ORDER_MODE_LIMIT,      // Pending Limit Order
    X_ORDER_MODE_STOP_LIMIT, // Pending Stop Limit Order
};

//
string ToString(ENUM_X_ORDER_MODES value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_ORDER_MODE_MARKET:
        result = "Market";
        break;

    //
    case X_ORDER_MODE_LIMIT:
        result = "Limit";
        break;

    //
    case X_ORDER_MODE_STOP:
        result = "Stop";
        break;

    //
    case X_ORDER_MODE_STOP_LIMIT:
        result = "StopLimit";
        break;
    }

    //
    return result;
}

//
// Select Positions Type ...
enum ENUM_X_POSITION_SELECT_METHODS
{
    X_POSITION_SELECT_NONE, // None
    X_POSITION_SELECT_MAX,  // Max
    X_POSITION_SELECT_MIN,  // Min
    X_POSITION_SELECT_BOTH, // Min and Max
    X_POSITION_SELECT_ALL,  // All
};

//
// Signal Execution Result ...
// note that some of them must be handles in
// EA's ...
enum ENUM_X_SIGNAL_EXECUTION_RESULT
{
    //
    X_SIGNAL_EXECUTION_UNKNOWN,                  // Unknown
    X_SIGNAL_EXECUTION_SUCCEED,                  // Succed
    X_SIGNAL_EXECUTION_FAILED_SPREAD,            // Spread more than Max Allowed
    X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED,       // Not Allowed Trade Type
    X_SIGNAL_EXECUTION_FAILED_NO_EQUITY,         // No Equity for Trade
    X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR,       // Price Error
    X_SIGNAL_EXECUTION_FAILED_PREV_PROFIT_ERROR, // Prev Profit Error
    X_SIGNAL_EXECUTION_FAILED_MAX_REACHED,       // Max Allowed Positions Reached
    X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED,  // Same Positions Delay Not Passed
    X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS,    // Invalid Params
};

//
enum ENUM_X_POSITION_TYPES
{
    X_POSITION_TYPE_NONE = 0,
    X_POSITION_TYPE_ALL = 1,
    X_POSITION_TYPE_LONG = 2,
    X_POSITION_TYPE_SHORT = 3,
};

//
bool IsValid(ENUM_X_POSITION_TYPES value)
{
    return value != X_POSITION_TYPE_NONE;
}
bool IsXValid(ENUM_X_POSITION_TYPES value)
{
    return IsValid(value);
}

//
bool HasSpecifiedType(ENUM_X_POSITION_TYPES value)
{
    //
    return value == X_POSITION_TYPE_LONG ||
           value == X_POSITION_TYPE_SHORT;
}

//
string ToString(ENUM_X_POSITION_TYPES value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_POSITION_TYPE_ALL:
        result = "All";
        break;

    //
    case X_POSITION_TYPE_NONE:
        result = "NONE";
        break;

    //
    case X_POSITION_TYPE_SHORT:
        result = "Short";
        break;

    //
    case X_POSITION_TYPE_LONG:
        result = "Long";
        break;
    }

    //
    return result;
}
string ToXString(ENUM_X_POSITION_TYPES value)
{
    return ToString(value);
}

//
bool IsLong(ENUM_X_POSITION_TYPES value)
{
    return value == X_POSITION_TYPE_LONG;
}

//
bool IsShort(ENUM_X_POSITION_TYPES value)
{
    return value == X_POSITION_TYPE_SHORT;
}

//
bool ToPositionType(
    ENUM_POSITION_TYPE &dest,
    ENUM_X_POSITION_TYPES source //
)
{
    //
    bool result = false;

    //
    result = source == X_POSITION_TYPE_LONG ||
             source == X_POSITION_TYPE_SHORT;
    if (!result)
    {
        return result;
    }

    //
    dest =
        source == X_POSITION_TYPE_LONG
            ? POSITION_TYPE_BUY
            : POSITION_TYPE_SELL;

    //
    return result;
}

//
ENUM_X_POSITION_TYPES ToPositionType(ENUM_POSITION_TYPE value)
{
    return IsLong(value)
               ? X_POSITION_TYPE_LONG
               : X_POSITION_TYPE_SHORT;
}

//
ENUM_X_POSITION_TYPES OppositPositionType(ENUM_POSITION_TYPE type)
{
    //
    ENUM_X_POSITION_TYPES result = X_POSITION_TYPE_NONE;

    //
    bool isLong = IsLong(type);
    bool isShort = IsShort(type);

    //
    result =
        //
        isLong
            ? X_POSITION_TYPE_SHORT
        : isShort
            ? X_POSITION_TYPE_LONG
            : X_POSITION_TYPE_NONE
        //
        ;

    //
    return result;
}

//
ENUM_X_DIRECTION ToDirection(ENUM_POSITION_TYPE value)
{
    //
    ENUM_X_DIRECTION result = X_DIRECTION_NONE;

    //
    result = IsLong(value)
                 ? X_DIRECTION_BULLISH
                 : X_DIRECTION_BEARISH;

    //
    return result;
}

//
ENUM_X_DIRECTION ToDirection(ENUM_X_POSITION_TYPES value)
{
    //
    ENUM_X_DIRECTION result = X_DIRECTION_NONE;

    //
    result = value == X_POSITION_TYPE_ALL
                 ? X_DIRECTION_ALL
             : value == X_POSITION_TYPE_LONG
                 ? X_DIRECTION_BULLISH
             : value == X_POSITION_TYPE_SHORT
                 ? X_DIRECTION_BEARISH
                 : X_DIRECTION_NONE;

    //
    return result;
}

//
// All Awailable Trade Restrictions ...
enum ENUM_XTRADE_RESTRICTION_PERIOD
{
    XTRADE_RESTRICATION_NONE,    // None
    XTRADE_RESTRICATION_HOURLY,  // Per Hour
    XTRADE_RESTRICATION_DAILY,   // Per Day
    XTRADE_RESTRICATION_WEEKLY,  // Per Week
    XTRADE_RESTRICATION_MONTHLY, // Per Month
};

//
string ToXString(ENUM_XTRADE_RESTRICTION_PERIOD value)
{
    //
    string result = NULL;

    //
    result = EnumToString(value);

    //
    return result;
}

//
ENUM_XTRADE_RESTRICTION_PERIOD ToXRestrictionPeriod(string value)
{
    //
    ENUM_XTRADE_RESTRICTION_PERIOD result = XTRADE_RESTRICATION_NONE;

    //
    if (!IsSpecifiedValid(value))
    {
        return result;
    }

    //
    // None ...
    if (value == ToXString(XTRADE_RESTRICATION_NONE))
    {
        result = XTRADE_RESTRICATION_NONE;
    }
    //
    // Per Hour ...
    else if (value == ToXString(XTRADE_RESTRICATION_HOURLY))
    {
        result = XTRADE_RESTRICATION_HOURLY;
    }
    //
    // Per Day ...
    else if (value == ToXString(XTRADE_RESTRICATION_DAILY))
    {
        result = XTRADE_RESTRICATION_DAILY;
    }
    //
    // Per Week ...
    else if (value == ToXString(XTRADE_RESTRICATION_WEEKLY))
    {
        result = XTRADE_RESTRICATION_WEEKLY;
    }
    //
    // Per Month ...
    else if (value == ToXString(XTRADE_RESTRICATION_MONTHLY))
    {
        result = XTRADE_RESTRICATION_MONTHLY;
    }

    //
    return result;
}

//
int GetXRestrictionsPeriodSeconds(ENUM_XTRADE_RESTRICTION_PERIOD value)
{
    //
    int result = 0;

    //
    if (!HasRestrictions(value))
    {
        return result;
    }

    //
    switch (value)
    {
    //
    // None ...
    case XTRADE_RESTRICATION_NONE:
        result = 0;
        break;

    //
    // Hour ...
    case XTRADE_RESTRICATION_HOURLY:
        result = PeriodSeconds(PERIOD_H1);
        break;

    //
    // Dayly ...
    case XTRADE_RESTRICATION_DAILY:
        result = PeriodSeconds(PERIOD_D1);
        break;

    //
    // Weekly ...
    case XTRADE_RESTRICATION_WEEKLY:
        result = PeriodSeconds(PERIOD_W1);
        break;

    //
    // Monthly ...
    case XTRADE_RESTRICATION_MONTHLY:
        result = PeriodSeconds(PERIOD_M1);
        break;
    }

    //
    return result;
}

/**
 * Check a Value Has Trade Restrictions or not ...
 *
 * @param  value: ENUM_XTRADE_RESTRICTION_PERIOD
 * ّ
 * @return ( bool )
 */
bool HasRestrictions(ENUM_XTRADE_RESTRICTION_PERIOD value)
{
    return value != XTRADE_RESTRICATION_NONE;
}

//
// All Awailable Trade Finalizations ...
enum ENUM_XTRADE_FINALIZATION
{
    XTRADE_FINAL_NONE,            // None
    XTRADE_FINAL_TP,              // TP
    XTRADE_FINAL_SL,              // SL
    XTRADE_FINAL_CLOSE_IN_PROFIT, // Profit
    XTRADE_FINAL_CLOSE_IN_LOSE,   // Lost
};

//
string ToXString(ENUM_XTRADE_FINALIZATION value)
{
    return EnumToString(value);
}

//
// Position Selecting Methods ...
enum ENUM_XPOSITION_SELECT_TYPES
{
    XPOSITION_SELECT_NONE = 0,                           // None
    XPOSITION_SELECT_ALL = 1,                            // All
    XPOSITION_SELECT_IN_PROFITS = 2,                     // In Profit Positions
    XPOSITION_SELECT_IN_DRAWDOWNS = 3,                   // In Drawdown Positions
    XPOSITION_SELECT_OLDEST = 4,                         // Oldest Position
    XPOSITION_SELECT_YOUNGEST = 5,                       // Youngest Position
    XPOSITION_SELECT_IN_PROFITS_OLDEST = 6,              // In Profit Positions Oldest one
    XPOSITION_SELECT_IN_PROFITS_YOUNGEST = 7,            // In Profit Positions Youngest one
    XPOSITION_SELECT_IN_PROFITS_MIN = 8,                 // Minimum Profitable Position
    XPOSITION_SELECT_IN_PROFITS_MAX = 9,                 // Maximum Profitable Position
    XPOSITION_SELECT_IN_DRAWDOWN_OLDEST = 10,            // In Drawdown Positions Oldest one
    XPOSITION_SELECT_IN_DRAWDOWN_YOUNGEST = 11,          // In Drawdown Positions Youngest one
    XPOSITION_SELECT_IN_DRAWDOWN_MIN = 12,               // Minimum in Drawdown Position
    XPOSITION_SELECT_IN_DRAWDOWN_MAX = 13,               // Maximum in Drawdown Position
    XPOSITION_SELECT_SAMES = 14,                         // Same Type Positions
    XPOSITION_SELECT_SAMES_OLDEST = 15,                  // Same Type Positions Oldest one
    XPOSITION_SELECT_SAMES_YOUNGEST = 16,                // Same Type Positions Youngest one
    XPOSITION_SELECT_OPPOSITS = 17,                      // Opposit Type Positions
    XPOSITION_SELECT_OPPOSIT_OLDEST = 18,                // Opposit Type Positions Oldest one
    XPOSITION_SELECT_OPPOSIT_YOUNGEST = 19,              // Opposit Type Positions Youngest one
    XPOSITION_SELECT_SAME_IN_PROFITS = 20,               // Same Type In Profit Positions
    XPOSITION_SELECT_SAME_IN_PROFITS_OLDEST = 21,        // Same Type In Profit Positions Oldest one
    XPOSITION_SELECT_SAME_IN_PROFITS_YOUNGEST = 22,      // Same Type In Profit Positions Youngest one
    XPOSITION_SELECT_SAME_IN_PROFITS_MIN = 23,           // Minimum Profitable of Same Type Positions
    XPOSITION_SELECT_SAME_IN_PROFITS_MAX = 24,           // Maximum Profitable of Same Type Positions
    XPOSITION_SELECT_OPPOSIT_IN_PROFITS = 25,            // Opposit Type In Profit Positions
    XPOSITION_SELECT_OPPOSIT_IN_PROFITS_OLDEST = 26,     // Opposit Type In Profit Positions Oldest one
    XPOSITION_SELECT_OPPOSIT_IN_PROFITS_YOUNGEST = 27,   // Opposit Type In Profit Positions Youngest one
    XPOSITION_SELECT_OPPOSIT_IN_PROFITS_MIN = 28,        // Minimum Profitable of Opposit Type Positions
    XPOSITION_SELECT_OPPOSIT_IN_PROFITS_MAX = 29,        // Maximum Profitable of Opposit Type Positions
    XPOSITION_SELECT_SAME_IN_DRAWDOWNS = 30,             // Same Type In Deawdown Positions
    XPOSITION_SELECT_SAME_IN_DRAWDOWN_OLDEST = 31,       // Same Type In Deawdown Positions Oldest one
    XPOSITION_SELECT_SAME_IN_DRAWDOWN_YOUNGEST = 32,     // Same Type In Deawdown Positions Youngest one
    XPOSITION_SELECT_SAME_IN_DRAWDOWN_MIN = 33,          // Minimum In Drawdown of Same Type Positions
    XPOSITION_SELECT_SAME_IN_DRAWDOWN_MAX = 34,          // Maximum In Drawdown of Same Type Positions
    XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWNS = 35,         // Opposit Type In Deawdown Positions
    XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_OLDEST = 36,   // Opposit Type In Deawdown Positions Oldest one
    XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_YOUNGEST = 37, // Opposit Type In Deawdown Positions Youngest one
    XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_MIN = 38,      // Minimum In Drawdown of Opposit Type Positions
    XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_MAX = 39,      // Maximum In Drawdown of Opposit Type Positions
};

/**
 * Validate Position Select Type ...
 *
 * @param  value: ENUM_XPOSITION_SELECT_TYPES
 *
 * @return ( bool )
 */
bool IsXValid(ENUM_XPOSITION_SELECT_TYPES value)
{
    return value != XPOSITION_SELECT_NONE;
}

//
string ToXString(ENUM_XPOSITION_SELECT_TYPES value)
{
    return EnumToString(value);
}

//
// Structs ...

//
struct XRR
{
    //
    double tps[];

    //
    double sl;
    double entry;

    //
    string prefix;
    string symbol;
    datetime time;
    ENUM_TIMEFRAMES period;

    //
    ENUM_X_POSITION_TYPES type;

    //
    // Constructor ...
    XRR()
    {
        Clean();
    }

    //
    // Tools ...

    //
    void Clean()
    {
        //
        sl = 0;
        entry = 0;

        //
        time = NULL;
        prefix = NULL;
        period = NULL;
        symbol = NULL;

        //
        type = X_POSITION_TYPE_NONE;

        //
        SpecifiedClean(tps);

        //
        ZeroMemory(this);
    }

    //
    bool Init(
        string _symbol,
        ENUM_TIMEFRAMES _period,
        double _sl,
        double _entry,
        ENUM_X_POSITION_TYPES _type,
        string _prefix = NULL //
    )
    {
        //
        bool result = false;

        //
        result =
            _sl > 0 &&
            _entry > 0 &&
            IsSpecifiedValid(_symbol) &&
            IsSpecifiedValid(_period) &&
            _type != X_POSITION_TYPE_ALL &&
            _type != X_POSITION_TYPE_NONE &&
            (_type == X_POSITION_TYPE_LONG
                 ? _entry > _sl
                 : _entry < _sl);
        if (!result)
        {
            return result;
        }

        //
        Default();

        //
        sl = _sl;
        type = _type;
        entry = _entry;
        symbol = _symbol;
        period = _period;
        prefix = _prefix;

        //
        time = TimeCurrent();

        //
        result = IsValid();

        //
        return result;
    }

    //
    void Default()
    {
        //
        Add(
            1.0,
            tps //
        );
        Add(
            1.5,
            tps //
        );
        Add(
            2.0,
            tps //
        );
        Add(
            3.0,
            tps //
        );
        Add(
            4.0,
            tps //
        );
        Add(
            5.0,
            tps //
        );
        Add(
            6.0,
            tps //
        );
    }

    //
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            sl > 0 &&
            entry > 0 &&
            IsSpecifiedValid(time) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period) &&
            IsValidSize(ArraySize(tps)) &&
            type != X_POSITION_TYPE_ALL &&
            type != X_POSITION_TYPE_NONE &&
            (type == X_POSITION_TYPE_LONG
                 ? entry > 0
                 : entry < sl);

        //
        return result;
    }

    //
    string GetTag()
    {
        //
        string result = NULL;

        //
        if (!IsValid())
        {
            //
            result = GetTypeName(this);
            return result;
        }

        //
        string sep = "_";

        //
        result =
            GetTypeName(this) + sep + (IsSpecifiedValid(prefix) ? prefix + sep : "") + symbol + sep + ToXString(period) + sep + ToFormatString(time) + sep + ToString(type);

        //
        return result;
    }
};

//
// Model a Target ...
struct XTarget
{
    //
    // Props ...
    bool doRF;               // Do RF on Target
    bool doRFOnEntry;        // Do RF on Entry
    double actingDistance;   // Acting Distances
    double target;           // Target Price for Actions
    double volumeMultiplier; // Volume Multiplier for Partial Close
    double tpValue;          // TP Value Changed ...

    //
    // Constructor ...
    XTarget()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        target = 0;
        tpValue = 0;
        doRF = false;
        actingDistance = 0;
        doRFOnEntry = false;
        volumeMultiplier = 0;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result = target > 0;

        //
        return result;
    }
};

//
// Get Lowest Target index ...
int GetLowest(XTarget &targets[])
{
    //
    int result = -1;

    //
    if (!HasChild(targets))
    {
        return result;
    }

    //
    XTarget iTarget;
    int count = ArraySize(targets);
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !iTarget.IsValid()
                ? true
                : iTarget.target > targets[i].target;
        if (canSet)
        {
            //
            result = i;
            iTarget = targets[i];
        }
    }

    //
    iTarget.Clean();

    //
    return result;
}

//
// Get Highest Target index ...
int GetHighest(XTarget &targets[])
{
    //
    int result = -1;

    //
    if (!HasChild(targets))
    {
        return result;
    }

    //
    XTarget iTarget;
    int count = ArraySize(targets);
    for (int i = 0; i < count; i++)
    {
        //
        bool canSet =
            !iTarget.IsValid()
                ? true
                : iTarget.target < targets[i].target;
        if (canSet)
        {
            //
            result = i;
            iTarget = targets[i];
        }
    }

    //
    iTarget.Clean();

    //
    return result;
}

//
// Apply Sorting on Targets ...
void ApplySortOnTargets(
    ENUM_X_DIRECTION forDir, // Target Sorting Direction ...
    XTarget &targets[]       // Targets to Apply Sorting ...
)
{
    //
    bool has = HasChild(targets) &&
               HasDirection(forDir);
    if (!has)
    {
        return;
    }

    //
    bool isBullish = IsBullish(forDir);
    bool isBearish = IsBearish(forDir);

    //
    XTarget tmp[];
    Copy(
        targets,
        tmp //
    );
    Clean(targets);
    while (HasChild(tmp))
    {
        //
        int idx = -1;
        if (isBullish)
        {
            idx = GetLowest(tmp);
        }
        else if (isBearish)
        {
            idx = GetHighest(tmp);
        }

        //
        has = IsValidIndex(idx);
        if (!has)
        {
            break;
        }

        //
        XTarget iTarget = tmp[idx];
        ArrayRemove(
            tmp,
            idx,
            1 //
        );
        AddRef(
            iTarget,
            targets //
        );
    }

    //
    Clean(tmp);
}

//
// Model an Open Position ...
struct XPosition
{
    //
    // Props ...

    //
    // Magic Number ...
    ulong magic;

    //
    // Ticket ID ...
    ulong ticket;

    //
    // Tradinng Symbol ...
    string symbol;

    //
    // Trading Period ...
    ENUM_TIMEFRAMES period;

    //
    // Position Type ...
    ENUM_POSITION_TYPE type;

    //
    // Take Profit ...
    double tp;

    //
    // Stop Loss ...
    double sl;

    //
    // Position Current Profit ...
    double profit;

    //
    // Position Open Price ...
    double entry;

    //
    // Current Symbol Price ...
    double price;

    //
    // Position Swap ...
    double swap;

    //
    // Position Commission ...
    double commission;

    //
    // Volume ...
    double volume;

    //
    // Position Open Time ...
    datetime openAt;

    //
    // Sifnal Provider ...
    string provider;

    //
    // Position Comment ...
    string comment;

    //
    // Constructor ...
    XPosition()
    {
        Clean();
    }

    //
    // Initializers ...

    bool ByIndex(int index)
    {
        //
        bool result = false;

        //
        static CPositionInfo mPositionInfo;

        //
        result = mPositionInfo.SelectByIndex(index);
        if (!result)
        {
            return result;
        }

        //
        ulong mTicket = mPositionInfo.Ticket();

        //
        result = ByTicket(mTicket);

        //
        return result;
    }

    bool ByTicket(ulong mTicket)
    {
        //
        bool result = false;

        //
        static CPositionInfo mPositionInfo;

        //
        result = mPositionInfo.SelectByTicket(mTicket);
        if (!result)
        {
            return result;
        }

        //
        swap = mPositionInfo.Swap();
        magic = mPositionInfo.Magic();
        openAt = mPositionInfo.Time();
        sl = mPositionInfo.StopLoss();
        ticket = mPositionInfo.Ticket();
        symbol = mPositionInfo.Symbol();
        tp = mPositionInfo.TakeProfit();
        profit = mPositionInfo.Profit();
        volume = mPositionInfo.Volume();
        comment = mPositionInfo.Comment();
        entry = mPositionInfo.PriceOpen();
        type = mPositionInfo.PositionType();
        price = mPositionInfo.PriceCurrent();
        commission = mPositionInfo.Commission();

        //
        period = ExtractPeriod(comment);
        provider = ExtractProvider(comment);

        //
        return result;
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        magic = 0;
        ticket = 0;
        tp = 0;
        sl = 0;
        swap = 0;
        entry = 0;
        price = 0;
        profit = 0;
        volume = 0;
        openAt = 0;
        commission = 0;

        //
        type = NULL;
        period = NULL;

        //
        symbol = NULL;
        comment = NULL;
        provider = NULL;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(symbol) &&
            NotEmpty(ticket)
            //
            ;

        //
        return result;
    }

    //
    bool IsLong()
    {
        return IsValid() &&
               IsXBullish(type);
    }

    //
    bool IsBullish()
    {
        return IsLong();
    }

    //
    bool IsShort()
    {
        return IsValid() &&
               IsXBearish(type);
    }

    //
    bool IsBearish()
    {
        return IsShort();
    }

    //
    ENUM_X_DIRECTION GetDirection()
    {
        //
        return IsBullish()
                   ? X_DIRECTION_BULLISH
               : IsBearish()
                   ? X_DIRECTION_BEARISH
                   : X_DIRECTION_NONE;
    }

    //
    // Retrieve Entry Price ...
    double GetEntry()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetXEntry(
            symbol,
            type
            //
        );

        //
        return result;
    }

    //
    // Retrieve Exit ...
    double GetExit()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetXExit(
            symbol,
            type
            //
        );

        //
        return result;
    }

    //
    // Get Spread by Point ...
    double GetSpread()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = PriceToPoint(GetXSpread(symbol));

        //
        return result;
    }

    //
    double GetPointsValue()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetPoints(symbol);

        //
        return result;
    }

    //
    double GetProfitInPoint()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);   // Tick size
        double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); // Tick value
        double pointSize = SymbolInfoDouble(symbol, SYMBOL_POINT);            // Point size
        double pointValue = tickValue * pointSize / tickSize;                 // Point value

        //
        result = profit / (volume * tickValue);

        //
        return result;
    }

    //
    // Find Risk ...
    double GetRisk()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return 0;
        }

        //
        result = MathAbs(entry - sl);

        //
        return result;
    }

    //
    double GetRiskInPoint()
    {
        //
        double result = 0;

        //
        result = GetRisk() / GetPoints();

        //
        return result;
    }

    //
    // Find Reward ...
    double GetReward()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = MathAbs(tp - entry);

        //
        return result;
    }

    //
    double GetRewardInPoint()
    {
        //
        double result = 0;

        //
        result = GetReward() / GetPoints();

        //
        return result;
    }

    //
    // Find Risk Reward Ratio ...
    double GetRiskRewardRatio()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetReward() / GetRisk();

        //
        return result;
    }

    //
    // Calculate TP Levels ...
    int CalculateTPLevels(double &tps[])
    {

        //
        int result = 0;

        //
        SpecifiedClean(tps);

        //
        bool isValid = IsValid();
        if (!isValid)
        {
            return result;
        }

        //
        double risk = GetRisk();
        int r2r = (int)GetRiskRewardRatio();
        bool isLong = IsLong();
        for (int i = 0; i < r2r; i++)
        {
            //
            double iValue =
                isLong
                    ? entry + risk + (i * risk)
                    : entry - risk - (i * risk);
            //
            Add(
                iValue,
                tps //
            );
        }

        //
        result = ArraySize(tps);

        //
        return result;
    }

    //
    // Calculate Reached TP Levels ...
    void CalculateReahedTP(
        double &reachedLevel,
        double &reachedPrice //
    )
    {
        //
        reachedLevel = -1;
        reachedPrice = 0;
        double tps[];
        bool isLong = IsLong();
        double exitPrice = GetExit();
        int tpLevels = CalculateTPLevels(tps);
        if (!IsValidSize(tpLevels) || exitPrice <= 0)
        {
            return;
        }

        //
        for (int i = 0; i < tpLevels - 1; i++)
        {
            //
            double cTP = tps[i];
            double nTP = tps[i + 1];

            //
            bool isReached =
                isLong
                    ? exitPrice > cTP &&
                          exitPrice < nTP
                    : exitPrice < cTP &&
                          exitPrice > nTP;
            if (isReached)
            {
                //
                reachedLevel = i + 1;
                reachedPrice = cTP;
            }
        }
    }

    //
    // Calculate Touched Reward ...
    double CalculateTouchedReward()
    {
        //
        double result = 0.0;

        //
        bool has = IsValid() &&
                   profit > 0;
        if (!has)
        {
            return result;
        }

        //
        double risk = MathAbs(entry - sl);
        double currentReward = MathAbs(price - entry);

        //
        result = currentReward / risk;

        //
        return result;
    }

    //
    // Calculate Age ...
    int GetAge(
        ENUM_TIMEFRAMES mPeriod // Calculate Age based on this period
    )
    {
        return GetXAge(this, mPeriod);
    }

    //
    // Check this instance is Same os Given instance or not ...
    bool IsSameAs(
        const XPosition &value // instance for Compare
    )
    {
        //
        bool result = false;

        //
        result =
            //
            type == value.type &&
            magic == value.magic &&
            entry == value.entry &&
            ticket == value.ticket &&
            volume == value.volume &&
            symbol == value.symbol &&
            provider == value.provider
            //
            ;

        //
        return result;
    }

    //
    // Find index in a Collection ...
    int FindIndex(
        XPosition &values[] // Collection to Find
    )
    {
        //
        return FindXIndex(
            this,
            values
            //
        );
    }

    //
    // Check Items Passed Specific Filters or not ...
    bool IsFiltersPassed(
        string mSymbol = NULL,              // Trading Symbol
        string mProvider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES mPeriod = NULL,     // Trading Timeframe
        ENUM_X_POSITION_TYPES mType = NULL, // Long (Buy), Short (Sell)
        ulong mMagic = NULL,                // Magic Number
        bool mFilterByMagic = true          // Get Only Self Open Positions
    )
    {
        //
        bool result = false;

        //
        result =
            //
            // Magic Filter ...
            IsMagicFilterPassed(
                mMagic,
                mFilterByMagic,
                this
                //
                )
            //
            &&
            //
            // Symbol Filter ...
            IsSymbolFilterPassed(
                mSymbol,
                this
                //
                )
            //
            &&
            //
            // Provider Filter ...
            IsProviderFilterPassed(
                mProvider,
                this
                //
                )
            //
            &&
            //
            // Period Filter ...
            IsPeriodFilterPassed(
                mPeriod,
                this
                //
                )
            //
            &&
            //
            // Type Filter ...
            IsTypeFilterPassed(
                mType,
                this
                //
                )
            //
            ;

        //
        return result;
    }
};

//
// Model a Signal ...
struct XSignal
{
    //
    // Props ...

    //
    ulong positionId; // if Executed this filled ...

    //
    datetime time; // Issue Time ...

    //
    double tp;     // Take Profit ...
    double sl;     // Stop Loss ...
    double entry;  // Exntry or Execution Price ...
    double volume; // Position Volume ...

    //
    string symbol;   // Trading Symbol ...
    string comment;  // Comment ...
    string provider; // Signaller ...

    //
    ENUM_TIMEFRAMES period;  // Trading TimeFrame ...
    ENUM_POSITION_TYPE type; // Long (Buy) or Short (Sell) ...
    ENUM_X_ORDER_MODES mode; // Execution Mode ...

    //
    string conditions; // Signal Conditions ...

    //
    // Additional ...

    //
    // Traget ...
    XTarget targets[];    // Provided Targets ...
    int appliedTargetIDX; // Last Applied Target Index ...
    bool ignoreEAVolume;  // Ignore EA Volume ...
    bool isTargetApplied; // Check if Target Applied or not ...

    //
    // Constructor ...
    XSignal()
    {
        Default();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        entry = 0;
        volume = 0;
        positionId = 0;

        //
        time = NULL;
        type = NULL;
        mode = NULL;
        symbol = NULL;
        period = NULL;
        comment = NULL;
        provider = NULL;
        conditions = NULL;

        //
        SpecifiedClean(targets);
        appliedTargetIDX = -1;
        ignoreEAVolume = false;
        isTargetApplied = false;

        //
        ZeroMemory(this);
    }

    //
    // Default Values ...
    void Default()
    {
        //
        entry = 0;
        volume = 0;
        positionId = 0;

        //
        time = NULL;
        type = NULL;
        mode = NULL;
        symbol = NULL;
        period = NULL;
        comment = NULL;
        provider = NULL;
        conditions = NULL;

        //
        SpecifiedClean(targets);
        appliedTargetIDX = -1;
        ignoreEAVolume = false;
        isTargetApplied = false;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(period) &&
            IsSpecifiedValid(symbol) &&
            NotEmpty(volume) &&
            NotEmpty(entry)
            //
            ;

        //
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // DO All Calculations and Normalizations ...
    bool Normalize()
    {
        //
        bool result = false;

        //
        // Normalizations ...
        symbol = NormalizeSymbol(symbol);
        period = NormalizePeriod(period);

        //
        time = NormalizeTime(time);

        //
        sl = NormalizePrice(sl, symbol);
        tp = NormalizePrice(tp, symbol);
        entry = NormalizePrice(entry, symbol);
        volume = NormalizeVolume(volume, symbol);

        //
        result = IsValid();

        //
        return result;
    }

    //
    // Check Signal Executed or not ...
    bool IsExecuted()
    {
        //
        bool result = false;

        //
        result =
            //
            IsValid() &&
            NotEmpty(positionId)
            //
            ;

        //
        return result;
    }

    //
    // Generate Comment for Signal ...
    string GenerateComment()
    {
        //
        string result = NULL;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        if (IsSpecifiedValid(provider))
        {
            //
            // Generate Provider Tag ...
            result += GenerateProviderTag(provider);
        }

        //
        if (IsSpecifiedValid(period))
        {
            //
            // Generate Period Tag ...
            result += GeneratePeriodTag(period);
        }

        //
        return result;
    }

    //
    int GetAge()
    {
        //
        return GetXAge(
            this,
            this.period //
        );
    }

    //
    // Find Risk ...
    double GetRisk()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return 0;
        }

        //
        result = MathAbs(entry - sl);

        //
        return result;
    }

    //
    // Find Reward ...
    double GetReward()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = MathAbs(tp - entry);

        //
        return result;
    }

    //
    // Find Risk Reward Ratio ...
    double GetRiskRewardRatio()
    {
        //
        double result = 0;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = GetReward() / GetRisk();

        //
        return result;
    }

    //
    string GetTag()
    {
        //
        string result = NULL;

        //
        result = "XSignal_" +
                 symbol + "_" +
                 ToXString(period) + "_" +
                 ToXString(type) + "_" +
                 ToXString(sl) + "_" +
                 ToXString(tp) + "_" +
                 ToXString(entry);

        //
        return result;
    }

    //
    string GetObjectID()
    {
        //
        string result = "";

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = "XSignal_" +
                 symbol + "_" +
                 ToXString(period) + "_" +
                 ToXString(type) + "_" +
                 ToXString(TimeToSeconds(time));

        //
        return result;
    }

    //
    // Retrieve String Representation of Signal ...
    string ToString()
    {
        //
        string result = NULL;

        //
        result =
            //
            ToXString("Symbol", symbol) +
            ToXString("Period", period) +
            ToXString("Provider", provider) +
            ToXString("Type", type) +
            ToXString("Mode", mode) +
            ToXString("Volume", volume) +
            ToXString("Entry", entry) +
            ToXString("TP", tp) +
            ToXString("SL", sl) +
            ToXString("Time", time) +
            ToXString("Comment", comment) +
            //
            ""
            //
            ;

        //
        return result;
    }

    //
    bool IsSameAs(XSignal &item)
    {
        //
        bool result = false;

        //
        result =
            //
            IsValid() &&
            item.IsValid() &&
            //
            tp == item.tp &&
            sl == item.sl &&
            mode == item.mode &&
            time == item.time &&
            type == item.type &&
            entry == item.entry &&
            symbol == item.symbol &&
            period == item.period &&
            provider == item.provider
            //
            ;

        //
        return result;
    }

    //
    string GetMessage(string action = NULL)
    {
        //
        string result = NULL;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result = ToXString(type) +
                 " Signal" +
                 (!IsSpecifiedValid(action)
                      ? ""
                      : " " + action) +
                 " by: " + provider +
                 " on (" + symbol + ")" +
                 " in: " + ToXString(period);

        //
        return result;
    }

    //
    string GetFileName()
    {
        //
        string result = NULL;

        //
        if (!IsValid())
        {
            return result;
        }

        //
        result =
            //
            symbol + "\\" +
            ToXString(type) + "\\" +
            ToXString(period) + "_" +
            ToFormatString(time)
            //
            ;

        //
        return result;
    }
};

//
// Model an Order ...
struct XOrder
{
    //
    // Time of order placement ...
    datetime setupAt;

    //
    // Time of order expiration ...
    datetime expiredAt;

    //
    // Time of order execution or cancellation ...
    datetime executedAt;

    //
    // Order type ...
    ENUM_ORDER_TYPE type;

    //
    // Order state ...
    ENUM_ORDER_STATE state;

    //
    // Type of execution by remainder ...
    ENUM_ORDER_TYPE_FILLING filling;

    //
    // Order lifetime ...
    ENUM_ORDER_TYPE_TIME lifetime;

    //
    // Order ticket ...
    ulong ticket;

    //
    // Magic Number ...
    long magic;

    //
    // Position id, that is placed on order,
    // when it is executed. Each executed order invokes a
    // deal, that opens new or changes existing
    // position. Id of that position is placed on
    // executed order in this moment.
    long positionId;

    //
    // Initial volume on order placement ...
    double initialVolume;

    //
    // Unfilled volume ...
    double currentVolume;

    //
    // Price, specified in the order ...
    double openPrice;

    //
    // Current price by order symbol ...
    double currentPrice;

    //
    // Price of placing Limit order when StopLimit order is triggered ...
    double stopLimitPrice;

    //
    // Take Profit level ...
    double tp;

    //
    // Stop Loss level ...
    double sl;

    //
    // Trading Symbol ...
    string symbol;

    //
    // Signal Provider ...
    string provider;

    //
    // Order Comment ...
    string comment;

    //
    ENUM_TIMEFRAMES period;

    //
    // Constructor ...
    XOrder()
    {
        Clean();
    }

    //
    // Initializers ...
    bool ByIndex(
        int index // Fill By Index ...
    )
    {
        //
        bool result = false;

        //
        int ordersCount = OrdersTotal();
        if (ordersCount <= 0)
        {
            return result;
        }

        //
        static COrderInfo mOrderInfo;

        //
        result = mOrderInfo.SelectByIndex(index);
        if (!result)
        {
            return result;
        }

        //
        // Reading Order Info ...
        magic = mOrderInfo.Magic();
        sl = mOrderInfo.StopLoss();
        tp = mOrderInfo.TakeProfit();
        ticket = mOrderInfo.Ticket();
        openPrice = mOrderInfo.PriceOpen();
        positionId = mOrderInfo.PositionId();
        currentPrice = mOrderInfo.PriceCurrent();
        initialVolume = mOrderInfo.VolumeInitial();
        currentVolume = mOrderInfo.VolumeCurrent();
        stopLimitPrice = mOrderInfo.PriceStopLimit();

        //
        symbol = mOrderInfo.Symbol();
        comment = mOrderInfo.Comment();

        //
        state = mOrderInfo.State();
        type = mOrderInfo.OrderType();
        lifetime = mOrderInfo.TypeTime();
        filling = mOrderInfo.TypeFilling();

        //
        setupAt = mOrderInfo.TimeSetup();
        executedAt = mOrderInfo.TimeDone();
        expiredAt = mOrderInfo.TimeExpiration();

        //
        period = ExtractPeriod(comment);
        provider = ExtractProvider(comment);

        //
        result = IsValid();

        //
        return result;
    }

    //
    // Retrieve Specific History Order ...
    bool HistoryByIndex(
        int index // Fill By Index ...
    )
    {
        //
        bool result = false;

        //
        int ordersCount = HistoryOrdersTotal();
        if (ordersCount <= 0)
        {
            return result;
        }

        //
        static CHistoryOrderInfo mOrderInfo;

        //
        result = mOrderInfo.SelectByIndex(index);
        if (!result)
        {
            return result;
        }

        //
        // Reading Order Info ...
        magic = mOrderInfo.Magic();
        sl = mOrderInfo.StopLoss();
        tp = mOrderInfo.TakeProfit();
        ticket = mOrderInfo.Ticket();
        openPrice = mOrderInfo.PriceOpen();
        positionId = mOrderInfo.PositionId();
        currentPrice = mOrderInfo.PriceCurrent();
        initialVolume = mOrderInfo.VolumeInitial();
        currentVolume = mOrderInfo.VolumeCurrent();
        stopLimitPrice = mOrderInfo.PriceStopLimit();

        //
        symbol = mOrderInfo.Symbol();
        comment = mOrderInfo.Comment();

        //
        state = mOrderInfo.State();
        type = mOrderInfo.OrderType();
        lifetime = mOrderInfo.TypeTime();
        filling = mOrderInfo.TypeFilling();

        //
        setupAt = mOrderInfo.TimeSetup();
        executedAt = mOrderInfo.TimeDone();
        expiredAt = mOrderInfo.TimeExpiration();

        //
        period = ExtractPeriod(comment);
        provider = ExtractProvider(comment);

        //
        result = IsValid();

        //
        return result;
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        tp = 0;
        sl = 0;
        magic = 0;
        ticket = 0;
        positionId = 0;
        openPrice = 0;
        currentPrice = 0;
        initialVolume = 0;
        currentVolume = 0;
        stopLimitPrice = 0;

        //
        setupAt = NULL;
        expiredAt = NULL;
        executedAt = NULL;

        //
        symbol = NULL;
        comment = NULL;
        provider = NULL;

        //
        type = NULL;
        state = NULL;
        period = NULL;
        filling = NULL;
        lifetime = NULL;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(symbol) &&
            NotEmpty(ticket) &&
            NotEmpty(positionId)
            //
            ;

        //
        return result;
    }

    //
    // Calculate Age ...
    int GetAge(
        ENUM_TIMEFRAMES mPeriod // Calculate Age based on this period
    )
    {
        return GetXAge(this, mPeriod);
    }

    //
    // Check this instance is Same os Given instance or not ...
    bool IsSameAs(
        const XOrder &value // instance for Compare
    )
    {
        //
        bool result = false;

        //
        result =
            //
            type == value.type &&
            state == value.state &&
            magic == value.magic &&
            ticket == value.ticket &&
            symbol == value.symbol &&
            provider == value.provider &&
            positionId == value.positionId
            //
            ;

        //
        return result;
    }

    //
    // Find index in a Collection ...
    int FindIndex(
        XOrder &values[] // Collection to Find
    )
    {
        //
        return FindXIndex(
            this,
            values
            //
        );
    }

    //
    // Check Items Passed Specific Filters or not ...
    bool IsFiltersPassed(
        string mSymbol = NULL,              // Trading Symbol
        string mProvider = NULL,            // Signal Provider
        ENUM_TIMEFRAMES mPeriod = NULL,     // Trading Timeframe
        ENUM_X_POSITION_TYPES mType = NULL, // Long (Buy), Short (Sell)
        ENUM_ORDER_STATE mState = NULL,     // Order State
        ulong mMagic = NULL,                // Magic Number
        bool mFilterByMagic = true          // Get Only Self Open Positions
    )
    {
        //
        bool result = false;

        //
        result =
            //
            // Magic Filter ...
            IsMagicFilterPassed(
                mMagic,
                mFilterByMagic,
                this
                //
                )
            //
            &&
            //
            // Symbol Filter ...
            IsSymbolFilterPassed(
                mSymbol,
                this
                //
                )
            //
            &&
            //
            // Provider Filter ...
            IsProviderFilterPassed(
                mProvider,
                this
                //
                )
            //
            &&
            //
            // Period Filter ...
            IsPeriodFilterPassed(
                mPeriod,
                this
                //
                )
            //
            &&
            //
            // Type Filter ...
            IsTypeFilterPassed(
                mType,
                this
                //
                )
            //
            &&
            //
            // State Filter ...
            IsStateFilterPassed(
                mState,
                this
                //
                )
            //
            ;

        //
        return result;
    }
};

//
// Model a Deal ...
struct XDeal
{
    //
    //  the ID of the Expert Advisor, that executed the deal ...
    ulong magic;

    //
    // the ID of position, in which the deal was involved ...
    ulong positionId;

    //
    // Ticket ...
    ulong ticket;

    //
    // the name of the deal symbol ...
    string symbol;

    //
    // order by which the deal is executed ...
    ulong order;

    //
    // the time of deal execution ...
    datetime time;

    //
    // Deal price ...
    double price;

    //
    // the financial result of the deal (in deposit currency) ...
    double profit;

    //
    // the amount of swap when position is closed ...
    double swap;

    //
    // the amount of commission of the deal ...
    double commission;

    //
    // the volume of deal ...
    double volume;

    //
    // the deal comment ...
    string comment;

    //
    string provider;

    //
    ENUM_TIMEFRAMES period;

    //
    // the deal type ...
    // ------------------------------------------------------------------
    // DEAL_TYPE_BUY => Buy ...
    // DEAL_TYPE_SELL => Sell ...
    // DEAL_TYPE_BUY_CANCELED => Canceled buy deal ...
    // There can be a situation when a previously executed buy deal is canceled. In this case,
    // the type of the previously executed deal (DEAL_TYPE_BUY) is changed to DEAL_TYPE_BUY_CANCELED,
    // and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated balance operation ...
    //
    // DEAL_TYPE_SELL_CANCELED => Canceled sell deal ...
    // There can be a situation when a previously executed sell deal is canceled. In this case,
    // the type of the previously executed deal (DEAL_TYPE_SELL) is changed to DEAL_TYPE_SELL_CANCELED,
    // and its profit/loss is zeroized. Previously obtained profit/loss is charged/withdrawn using a separated balance operation
    //
    // DEAL_TYPE_BALANCE => Balance ...
    // DEAL_TYPE_CREDIT => Credit ...
    // DEAL_TYPE_CHARGE => Additional charge ...
    // DEAL_TYPE_CORRECTION => Correction ...
    // DEAL_TAX => Tax charges ...
    // DEAL_TYPE_BONUS => Bonus ...
    // DEAL_TYPE_INTEREST => Interest rate ...
    // DEAL_DIVIDEND => Dividend operations...
    // DEAL_DIVIDEND_FRANKED => Franked (non-taxable) dividend operations ...
    // DEAL_TYPE_COMMISSION => Additional commission ...
    // DEAL_TYPE_COMMISSION_DAILY => Daily commission ...
    // DEAL_TYPE_COMMISSION_MONTHLY => Monthly commission ...
    // DEAL_TYPE_COMMISSION_AGENT_DAILY => Daily agent commission ...
    // DEAL_TYPE_COMMISSION_AGENT_MONTHLY => Monthly agent commission
    ENUM_DEAL_TYPE type;

    //
    // the deal direction ...
    // ----------------------------------
    // DEAL_ENTRY_IN => Entry in ...
    // DEAL_ENTRY_OUT => Entry out ...
    // DEAL_ENTRY_INOUT => Reverse ...
    // DEAL_ENTRY_OUT_BY => Close a position by an opposite one ...
    ENUM_DEAL_ENTRY entry;

    //
    // deal reson ...
    // ---------------------
    // DEAL_REASON_SL => The deal was executed as a result of Stop Loss activation ...
    // DEAL_REASON_TP => The deal was executed as a result of Take Profit activation ...
    // DEAL_REASON_SO => The deal was executed as a result of the Stop Out event ...
    // DEAL_REASON_WEB => The deal was executed as a result of activation of an order placed from the web platform ...
    // DEAL_REASON_SPLIT => The deal was executed after the split (price reduction) of an instrument, which had an open position during split announcement ...
    // DEAL_REASON_CLIENT => The deal was executed as a result of activation of an order placed from a desktop terminal ...
    // DEAL_REASON_MOBILE => The deal was executed as a result of activation of an order placed from a mobile application ...
    // DEAL_REASON_EXPERT => The deal was executed as a result of activation of an order placed from an MQL5 program, i.e. an Expert Advisor or a script ...
    // DEAL_REASON_VMARGIN => The deal was executed after charging the variation margin ...
    // DEAL_REASON_ROLLOVER => The deal was executed due to a rollover ...
    ENUM_DEAL_REASON reason;

    //
    // Constructor ...
    XDeal()
    {
        Clean();
    }

    //
    // Initializers ...
    bool ByIndex(
        int index // Deal Index
    )
    {
        //
        bool result = false;

        //
        int dealsCount = HistoryDealsTotal();
        result = dealsCount > 0;
        if (!result)
        {
            return result;
        }

        //
        static CDealInfo mDealInfo;

        //
        result = mDealInfo.SelectByIndex(index);
        if (!result)
        {
            //
            return result;
        }

        //
        // Reading Order Info ...
        swap = mDealInfo.Swap();
        magic = mDealInfo.Magic();
        order = mDealInfo.Order();
        ticket = mDealInfo.Ticket();
        time = mDealInfo.Time();
        price = mDealInfo.Price();
        symbol = mDealInfo.Symbol();
        profit = mDealInfo.Profit();
        volume = mDealInfo.Volume();
        comment = mDealInfo.Comment();
        positionId = mDealInfo.PositionId();
        entry = mDealInfo.Entry();
        commission = mDealInfo.Commission();
        type = mDealInfo.DealType();
        reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(
            ticket,
            DEAL_REASON);

        //
        period = ExtractPeriod(comment);
        provider = ExtractProvider(comment);

        //
        result = IsValid();

        //
        return result;
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        magic = 0;
        positionId = 0;
        ticket = 0;
        order = 0;
        time = 0;
        price = 0;
        profit = 0;
        swap = 0;
        commission = 0;
        volume = 0;

        //
        symbol = NULL;
        comment = NULL;
        provider = NULL;

        //
        type = NULL;
        entry = NULL;
        period = NULL;
        reason = NULL;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            IsSpecifiedValid(symbol) &&
            NotEmpty(ticket) &&
            NotEmpty(positionId)
            //
            ;

        //
        return result;
    }

    //
    // Calculate Age ...
    int GetAge(
        ENUM_TIMEFRAMES mPeriod // Calculate Age based on this period
    )
    {
        return GetXAge(this, mPeriod);
    }

    //
    // Check this instance is Same os Given instance or not ...
    bool IsSameAs(
        const XDeal &value // instance for Compare
    )
    {
        //
        bool result = false;

        //
        result =
            //
            type == value.type &&
            magic == value.magic &&
            entry == value.entry &&
            reason == value.reason &&
            ticket == value.ticket &&
            volume == value.volume &&
            symbol == value.symbol &&
            provider == value.provider &&
            positionId == value.positionId
            //
            ;

        //
        return result;
    }

    //
    // Find index in a Collection ...
    int FindIndex(
        XDeal &values[] // Collection to Find
    )
    {
        //
        return FindXIndex(
            this,
            values
            //
        );
    }

    //
    // Check Items Passed Specific Filters or not ...
    bool IsFiltersPassed(
        string mSymbol = NULL,           // Trading Symbol
        string mProvider = NULL,         // Signal Provider
        ENUM_TIMEFRAMES mPeriod = NULL,  // Trading Timeframe
        ENUM_DEAL_TYPE mType = NULL,     // Deal Type
        ENUM_DEAL_ENTRY mEntry = NULL,   // Deal Entry
        ENUM_DEAL_REASON mReason = NULL, // Deal Reason
        ulong mMagic = NULL,             // Magic Number
        bool mFilterByMagic = true       // Get Only Self Open Positions
    )
    {
        //
        bool result = false;

        //
        result =
            //
            // Magic Filter ...
            IsMagicFilterPassed(
                mMagic,
                mFilterByMagic,
                this
                //
                )
            //
            &&
            //
            // Symbol Filter ...
            IsSymbolFilterPassed(
                mSymbol,
                this
                //
                )
            //
            &&
            //
            // Provider Filter ...
            IsProviderFilterPassed(
                mProvider,
                this
                //
                )
            //
            &&
            //
            // Period Filter ...
            IsPeriodFilterPassed(
                mPeriod,
                this
                //
                )
            //
            &&
            //
            // Type Filter ...
            IsTypeFilterPassed(
                mType,
                this
                //
                )
            //
            &&
            //
            // Entry Filter ...
            IsEntryFilterPassed(
                mEntry,
                this
                //
                )
            //
            &&
            //
            // Reason Filter ...
            IsReasonFilterPassed(
                mReason,
                this
                //
                )
            //
            ;

        //
        return result;
    }
};

//
// Model OnTrade Event Handler Result on XCTrade Class ...
struct XOnTradeHandlerState
{
    //
    bool hasNewDeal;
    int newDeals;

    //
    bool hasNewOrder;
    int newOrders;

    //
    bool hasNewPosition;
    int newPositions;

    //
    bool hasNewHistoryOrder;
    int newHistoryOrders;

    //
    // Constructor ...
    XOnTradeHandlerState()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        hasNewDeal = false;
        hasNewOrder = false;
        hasNewPosition = false;
        hasNewHistoryOrder = false;

        //
        newDeals = 0;
        newOrders = 0;
        newPositions = 0;
        newHistoryOrders = 0;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            //
            (
                //
                hasNewDeal
                    ? newDeals > 0
                    : true
                //
                )
            //
            &&
            //
            (
                //
                hasNewOrder
                    ? newOrders > 0
                    : true
                //
                )
            //
            &&
            //
            (
                //
                hasNewPosition
                    ? newPositions > 0
                    : true
                //
                )
            //
            &&
            //
            (
                //
                hasNewHistoryOrder
                    ? newHistoryOrders > 0
                    : true
                //
                )
            //
            ;

        //
        return result;
    }
};

//
// an Structure for Describe aTrading Session ...
struct XTradeSession
{
    //
    // Properties ...
    string start; // Session Start Time (hh:mm)
    string end;   // Session End Time (hh:mm)
    string name;  // Session Name

    //
    // Constructor ...
    XTradeSession()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        end = NULL;
        name = NULL;
        start = NULL;

        //
        ZeroMemory(this);
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result = IsSpecifiedValid(start) &&
                 IsSpecifiedValid(end);
        if (!result)
        {
            return result;
        }

        //
        return result;
    }

    //
    // Check Session is Active or Not ...
    bool IsActive(datetime time = NULL)
    {
        //
        bool result = false;

        //
        time = NormalizeTime(time);

        //
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        result = IsTimeInRange(
            time,
            start,
            end //
        );

        //
        return result;
    }
};

//
// an Structure for Describe an Specified Symbols
// Trading Sessions ...
struct XSymbolTradeConfig
{
    //
    // Propeties ...

    //
    int longs;                                         // All Long Trades
    int shorts;                                        // All Short Trades
    int trades;                                        // All Trades
    int losses;                                        // All Loosing Trades
    int profits;                                       // All Winning Trades
    string name;                                       // Provider Name
    string symbol;                                     // Trading Symbol
    int managedSLs;                                    // Managed SL(s)
    int managedTPs;                                    // Managed TP(s)
    int managedTrades;                                 // Managed Trades
    int maxAllowedTPs;                                 // Max Allowed TP(s) in Restrictions Period
    int maxAllowedSLs;                                 // Max Allowed SL(s) in Restrictions Period
    int maxAllowedTrades;                              // Max Allowed Trades in Restrictions Period
    datetime managedStart;                             // Managed Start Time
    double managedBalance;                             // Managed Start Balance
    double managedProfits;                             // Managed Profit(s)
    ENUM_TIMEFRAMES period;                            // Trading Period
    int maxAllowedPositions;                           // Max Allowed Same Time Trades
    double maxAllowedSpread;                           // Max Allowed Spread For Trade
    bool isForceCloseAtTime;                           // a Flag for Determines Force Closed or not
    XTradeSession sessions[];                          // Trading Sessions
    string closeAllOpenTradesAt;                       // Close All Open Trades per Day At Specified Time
    int restrictionsPeriodMultiplier;                  // Specified Restrictions Period Multiplier
    ENUM_XTRADE_RESTRICTION_PERIOD restrictionsPeriod; // Trade Restrictions Period

    //
    // Constructor ...
    XSymbolTradeConfig()
    {
        Clean();
    }

    //
    // Tools ...

    //
    // Cleanup ...
    void Clean()
    {
        //
        name = NULL;
        symbol = NULL;
        period = NULL;
        SpecifiedClean(sessions);

        //
        longs = 0;
        shorts = 0;
        trades = 0;
        losses = 0;
        profits = 0;

        //
        managedStart = NULL;

        //
        managedSLs = 0;
        managedTPs = 0;
        managedTrades = 0;

        //
        managedBalance = 0;
        managedProfits = 0;

        //
        maxAllowedTPs = 0;
        maxAllowedSLs = 0;
        maxAllowedTrades = 0;
        maxAllowedSpread = 25;
        maxAllowedPositions = 5;
        closeAllOpenTradesAt = "";
        isForceCloseAtTime = false;
        restrictionsPeriod = XTRADE_RESTRICATION_NONE;

        //
        ZeroMemory(this);
    }

    /**
     * Initial ...
     *
     * @param  _symbol: Trading Symbol
     * @param  _period: Trading Period
     * @param  _sessionsDescriptor: Sessions Description
     * @param  _maxAllowedSpread: Max Allowed Spread for Open Trade
     * @param  _maxAllowedPositions: Max Allowed Same Time Position
     * @param  _restrictionsDescriptor: Restrictions Descriptor
     *
     * @return ( bool )
     */
    bool Init(
        string _name,
        string _symbol = NULL,
        ENUM_TIMEFRAMES _period = NULL,
        string _sessionsDescriptor = NULL,
        double _maxAllowedSpread = 25,
        int _maxAllowedPositions = 5,
        string _restrictionsDescriptor = NULL //
    )
    {
        //
        bool result = false;

        //
        // Normalize Symbol ...
        _symbol = NormalizeSymbol(_symbol);
        _period = NormalizePeriod(_period);

        //
        name = _name;
        symbol = _symbol;
        period = _period;
        maxAllowedSpread = _maxAllowedSpread;
        maxAllowedPositions = _maxAllowedPositions;

        //
        // Parse Sessions ...
        ParseSessions(_sessionsDescriptor);

        //
        // Parse Restrictions ...
        ParseRestrictions(_restrictionsDescriptor);

        //
        // Check Validation ...
        result = IsValid();

        //
        return result;
    }

    //
    // Check Same ...
    bool IsSame(XSymbolTradeConfig &dest)
    {
        //
        bool result = false;

        //
        // Validate ...
        result = IsValid() &&
                 dest.IsValid() &&
                 name == dest.name &&
                 symbol == dest.symbol &&
                 period == dest.period;

        //
        return result;
    }

    //
    // Validate ...
    bool IsValid()
    {
        //
        bool result = false;

        //
        result =
            IsSpecifiedValid(name) &&
            IsSpecifiedValid(symbol) &&
            IsSpecifiedValid(period);

        //
        return result;
    }

    //
    // Check Allow Trade in Active Sessions ...
    bool CanTrade(datetime time = NULL)
    {
        //
        bool result = false;

        //
        // Normalize Args ...
        time = NormalizeTime(time);

        //
        // Validating ...
        result = IsValid();
        if (!result)
        {
            return result;
        }

        //
        // if there is not any defined sessions for Symbol ...
        // it's allow Trading in all times ...
        result = !HasChild(sessions);
        if (result)
        {
            return result;
        }

        //
        // Detect an Active Session ...
        int count = ArraySize(sessions);
        for (int i = 0; i < count; i++)
        {
            //
            result = sessions[i].IsActive(time);
            if (result)
            {
                break;
            }
        }

        //
        return result;
    }

    //
    // Checking Restrictions ...
    bool IsRestricted(datetime time = NULL)
    {
        //
        bool result = false;

        //
        // Normalize Time ...
        time = NormalizeTime(time);

        //
        // Check Has Restrictions ...
        bool has = HasRestrictions(restrictionsPeriod);
        result = !has;
        if (result)
        {
            return result;
        }

        //
        // Check Restrictions Time ...
        has = IsSpecifiedValid(managedStart) &&
              time <= managedStart;
        result = !has;
        if (result)
        {
            return result;
        }

        //
        // Validate Restrictions Timing ...
        // Check Restrictions ...

        //
        bool isTPAllowed = maxAllowedTPs == 0
                               ? true
                               : managedTPs < maxAllowedTPs;
        bool isSLAllowed = maxAllowedSLs == 0
                               ? true
                               : managedSLs < maxAllowedSLs;
        bool isTradeAllowed = maxAllowedTrades == 0
                                  ? true
                                  : managedTrades < maxAllowedTrades;

        //
        result = isTPAllowed &&
                 isSLAllowed &&
                 isTradeAllowed;
        if (result)
        {
            return result;
        }

        //
        // Check Restrictions Reset ...
        HandleRestrictions();

        //
        return result;
    }

    //
    // Parse Sessions ...
    void ParseSessions(string descriptor)
    {
        //
        // Validate ...
        if (!IsSpecifiedValid(descriptor))
        {
            return;
        }

        //
        bool has = false;
        string _sessions[];
        int sessionsCount = 0;

        //
        // Extract Per Symbol Sessions ...
        sessionsCount = SplitContent(
            _sessions,
            descriptor,
            "," //
        );

        //
        // Try to Extract Start and End Times Series ...
        has = IsValidSize(sessionsCount);
        if (!has)
        {
            return;
        }

        //
        // Iterate Sessions ...
        for (int i = 0; i < sessionsCount; i++)
        {
            //
            // Ignore unused Cases and Extracts only Content ...
            string iSessionDescriptorStr = ExtractString(_sessions[i]);
            has = IsSpecifiedValid(iSessionDescriptorStr);
            if (has)
            {
                //
                // Extract Session Descriptor Data ...
                string parts[];
                int partsCount = SplitContent(
                    parts,
                    iSessionDescriptorStr,
                    "_" //
                );
                has = IsValidSize(partsCount) && partsCount == 3;
                if (has)
                {
                    //
                    string iName = parts[0];
                    string iStart = parts[1];
                    string iEnd = parts[2];
                    has = IsSpecifiedValid(iName) &&
                          IsSpecifiedValid(iStart) &&
                          IsSpecifiedValid(iEnd);
                    if (has)
                    {
                        //
                        // Set Active Session Per Symbols ...
                        XTradeSession session;

                        //
                        session.end = iEnd;
                        session.name = iName;
                        session.start = iStart;

                        //
                        AddRef(
                            session,
                            sessions //
                        );

                        //
                        session.Clean();
                    }
                }

                //
                SpecifiedClean(parts);
            }
        }

        //
        SpecifiedClean(_sessions);
    }

    //
    // Parse Restrictions ...
    void ParseRestrictions(string descriptor)
    {
        //
        // Validate ...
        if (!IsSpecifiedValid(descriptor))
        {
            return;
        }

        //
        // Extract Content ...
        descriptor = ExtractString(descriptor);
        if (!IsSpecifiedValid(descriptor))
        {
            return;
        }

        //
        int count = 0;
        bool has = false;
        string iRestrictionsParts[];

        //
        // Extract Parts ...
        count = SplitContent(
            iRestrictionsParts,
            descriptor,
            "," //
        );
        has = IsValidSize(count);
        if (!has)
        {
            //
            // Default Restrictions ...
            ApplyDefaultRestrictions();
            return;
        }

        //
        // Try To Extract Required Parts ...
        bool hasRestrictionsPeriod = count >= 1;
        bool hasRestrictionsPeriodMultiplier = count >= 2;
        bool hasMaxAllowedTPs = count >= 3;
        bool hasMaxAllowedSLs = count >= 4;
        bool hasMaxAllowedTrades = count >= 5;
        bool hasCloseAllOpenTradesAt = count >= 6;

        //
        // Restrictions Period ...
        restrictionsPeriod =
            hasRestrictionsPeriod
                ? ToXRestrictionPeriod(Trim(iRestrictionsParts[0]))
                : XTRADE_RESTRICATION_NONE;

        //
        // Restrictions Period Multiplier ...
        restrictionsPeriodMultiplier =
            hasRestrictionsPeriodMultiplier
                ? (int)(Trim(iRestrictionsParts[1]))
                : 1;

        //
        // Max Allowed TPs ...
        maxAllowedTPs =
            hasMaxAllowedTPs
                ? (int)(Trim(iRestrictionsParts[2]))
                : 0;

        //
        // Max Allowed SLs ...
        maxAllowedSLs =
            hasMaxAllowedSLs
                ? (int)(Trim(iRestrictionsParts[3]))
                : 0;

        //
        // Max Allowed Trades ...
        maxAllowedTrades =
            hasMaxAllowedTrades
                ? (int)(Trim(iRestrictionsParts[4]))
                : 0;

        //
        // Close All Open Trades At ...
        closeAllOpenTradesAt =
            hasCloseAllOpenTradesAt
                ? Trim(iRestrictionsParts[5])
                : NULL;
    }

    //
    // Apply Default Restrictions ...
    void ApplyDefaultRestrictions()
    {
        //
        maxAllowedTPs = 0;
        maxAllowedSLs = 0;
        maxAllowedTrades = 0;
        closeAllOpenTradesAt = NULL;
        restrictionsPeriod = XTRADE_RESTRICATION_NONE;
    }

    //
    // Handle Restrictions ...
    void HandleRestrictions(datetime time = NULL)
    {
        //
        // Normalize Time ...
        time = NormalizeTime(time);

        //
        // Check Restrictions Requirements ...
        bool has = HasRestrictions(restrictionsPeriod) &&
                   restrictionsPeriodMultiplier >= 1;
        if (!has)
        {
            return;
        }

        //
        // Check Restrictions Start ...
        has = IsSpecifiedValid(managedStart);
        if (!has)
        {
            managedStart = time;
        }

        //
        // Calculating Expiration Date based on Config ...
        int expiredDelay = restrictionsPeriodMultiplier * GetXRestrictionsPeriodSeconds(restrictionsPeriod);
        datetime expiredDate = (datetime)(TimeToSeconds(managedStart) + expiredDelay);

        //
        // Check Expiration ...
        has = time >= expiredDate;
        if (!has)
        {
            return;
        }

        //
        ResetRestrictions();
    }

    //
    // Reset Restrictions ...
    void ResetRestrictions()
    {
        //
        managedSLs = 0;
        managedTPs = 0;
        managedTrades = 0;
        managedBalance = 0;
        managedProfits = 0;
        managedStart = TimeCurrent();
    }

    //
};

//
// an Structure to Describe a Trade ...
struct XTradeInfo
{
    //
    // Props ...
    double swap;
    double profit;
    XSignal signal;
    ulong positionID;
    double commission;
    string conditions;
    double bullishScore;
    double bearishScore;
    ENUM_XTRADE_FINALIZATION finalize;

    //
    datetime signaledAt;
    datetime executedAt;
    datetime finalizedAt;

    //
    // Constructor ...
    XTradeInfo()
    {
        Clean();
    }

    //
    // Tools ...

    /**
     * Cleanup Model ...
     */
    void Clean()
    {
        //
        swap = 0;
        profit = 0;
        signal.Clean();
        commission = 0;
        positionID = 0;
        conditions = NULL;
        bullishScore = 0;
        bearishScore = 0;
        finalize = XTRADE_FINAL_NONE;

        //
        signaledAt = NULL;
        executedAt = NULL;
        finalizedAt = NULL;

        //
        ZeroMemory(this);
    }

    /**
     * Check Has Signal ...
     *
     * @return ( bool )
     */
    bool HasSignal()
    {
        //
        bool result = false;

        //
        result = signal.IsValid();

        //
        return result;
    }

    /**
     * Check Signal Executed or Not ...
     *
     * @return ( bool )
     */
    bool IsExecuted()
    {
        //
        bool result = false;

        //
        result = positionID > 0;

        //
        return result;
    }

    /**
     * Check a Trade is Finalized or not ...
     *
     * @return ( bool )
     */
    bool IsFinalized()
    {
        //
        bool result = false;

        //
        result = HasSignal() &&
                 IsExecuted() &&
                 finalize != XTRADE_FINAL_NONE;

        //
        return result;
    }

    /**
     * Generate Summary Info ...
     *
     * @return ( string )
     */
    string Summary()
    {
        //
        string result = NULL;

        //
        result =
            //
            ToXString("Swap", swap) +
            ToXString("Profit", profit) +
            ToXString("Commission", commission) +
            ToXString("Finalize", ToXString(finalize)) +
            ToXString("PositionID", positionID) +
            ToXString("----------------") + "\n" +
            ToXString("Signaled At", ToFormatString(signaledAt)) +
            ToXString("Executed At", ToFormatString(executedAt)) +
            ToXString("Finalized At", ToFormatString(finalizedAt))
            //
            ;

        //
        return result;
    }

    /**
     * Get Data Collection File Name ...
     *
     * @return ( string )
     */
    string GetFileName()
    {
        //
        string result = NULL;

        //
        bool hasSignal = HasSignal();
        bool isExecuted = IsExecuted();
        bool isFinalized = IsFinalized();
        if (!hasSignal && !isExecuted && !isFinalized)
        {
            return result;
        }

        //
        result =
            //
            signal.symbol + "\\" +
            ToString(signal.type) + "\\" +
            (profit >= 0 ? "Profit" : "Loss") + "\\" +
            ToString(positionID) + "_" +
            ToString(signal.period) + "_" +
            ToFormatString(signaledAt)
            //
            ;

        //
        return result;
    }

    /**
     * Converts Model to String Representation ...
     *
     * @param  onlySignals: Boolean, Specified Represent Only Signal or not ...
     *
     * @return ( string )
     */
    string ToString(
        bool includeSummary = true,
        bool includeSignal = true,
        bool includeConditions = true //
    )
    {
        //
        string result = NULL;

        //
        // Generating Model Summary ...
        string summary = Summary();

        //
        // Generating Signal Summary ...
        string signalSummary = signal.ToString();

        //
        if (includeSummary)
        {
            //
            result +=
                ToXString("----------------") + "\n" +
                ToXString("Summary: ") + "\n" +
                ToXString("----------------") + "\n" +
                summary + "\n";
        }

        //
        if (includeSignal)
        {
            //
            result +=
                ToXString("----------------") + "\n" +
                ToXString("Signal: ") + "\n" +
                ToXString("----------------") + "\n" +
                signalSummary + "\n";
        }

        //
        if (includeConditions)
        {
            //
            result +=
                ToXString("----------------") + "\n" +
                ToXString("Conditions: ") + "\n" +
                ToXString("----------------") + "\n" +
                conditions + "\n";
        }

        //
        return result;
    }

    string GetConditionsString()
    {
        //
        string result = NULL;

        //
        string conditionsStr =
            conditions;
        string condParts[];
        int condPartsCount = SplitContent(
            condParts,
            conditionsStr,
            "\n" //
        );
        if (IsValidSize(condPartsCount))
        {
            //
            conditionsStr = NULL;
            while (HasChild(condParts))
            {
                //
                string iStr = condParts[0];
                ArrayRemove(
                    condParts,
                    0,
                    1 //
                );

                //
                bool isLast = !HasChild(condParts);

                //
                bool isCond = Contains(
                    "is",
                    iStr,
                    false //
                );
                if (isCond)
                {
                    //
                    string iParts[];
                    int iPartsCount = SplitContent(
                        iParts,
                        iStr,
                        ":" //
                    );
                    if (iPartsCount == 2)
                    {
                        //
                        string iVal = iParts[1];
                        StringTrimLeft(iVal);
                        StringTrimRight(iVal);
                        bool isPassed = ToBoolean(iVal);
                        conditionsStr += (isPassed ? "" : "!") + "conditions." + iParts[0] + (!isLast ? " &&" : "") + "\n";
                    }
                }
            }
            SpecifiedClean(condParts);
        }
        else
        {
            conditionsStr = NULL;
        }

        //
        result =
            "(" + "\n" +
            "\\" + "\\" + " " + ToXString("Type", ToXString(signal.type)) +
            "\\" + "\\" + " " + ToXString("Profit", profit) +
            "\\" + "\\" + " " + ToXString("Finalize", ToXString(finalize)) +
            "\\" + "\\" + " " + ToXString("PositionID", positionID) +
            "\\" + "\\" + " " + ToXString("----------------") + "\n" +
            "\\" + "\\" + " " + ToXString("Signaled At", ToFormatString(signaledAt)) +
            "\\" + "\\" + " " + ToXString("Executed At", ToFormatString(executedAt)) +
            "\\" + "\\" + " " + ToXString("Finalized At", ToFormatString(finalizedAt)) +
            "\\" + "\\" + " " + ToXString("----------------") + "\n" +
            "\\" + "\\" + " " + ToXString("Scores:") + "\n" +
            "\\" + "\\" + " " + ToXString("Bullish", bullishScore) +
            "\\" + "\\" + " " + ToXString("Bearish", bearishScore) +
            "\\" + "\\" + " " + ToXString("----------------") + "\n" +
            conditionsStr +
            (IsValid(conditionsStr) ? "" : "") +
            ") && ";

        //
        return result;
    }
};

//
// TypeDefs ...

//
typedef void (*TOnSignal)(XSignal &signal);

//
typedef bool (*TCanAnalyse)(
    string _symbol,
    ENUM_TIMEFRAMES _period,
    datetime _time //
);

//
// an Event Type for Notify Guard Triggered ...
typedef void (*TOnGuarded)(
    ENUM_X_GUARD_ACTIONS guard,
    XPosition &positions[]);

//
typedef int (*TCheckForGuard)(
    XGuard &guards[],
    XPosition &positions[],
    int barIndex = 0 //
);

//
typedef void (*TOnDealsChanged)(int count);

//
typedef void (*TOnOrdersChanged)(int count);

//
typedef void (*TOnPositionsChanged)(int count);

//
typedef void (*TOnStopLoss)(const XDeal &deal);
typedef void (*TOnTakeProfit)(const XDeal &deal);

//
typedef void (*TOnTradeStateChanged)(const XOnTradeHandlerState &state);

//
typedef void (*TOnForceClose)(
    const ulong ticket,
    const XPosition &position,
    const string comment //
);

//
typedef void (*TOnPartialClose)(
    const ulong ticket,
    const double profit,
    const string comment //
);

//
typedef void (*TOnModify)(
    const ulong ticket,
    const double profit,
    const string comment //
);

//
// Classes ...

//
// Tools ...

//
bool IsValid(ENUM_X_ORDER_MODES value)
{
    return value != X_ORDER_MODE_NOTHING;
}

//
bool IsXBullish(ENUM_POSITION_TYPE type)
{
    return type == POSITION_TYPE_BUY;
}

//
bool IsXBullish(ENUM_X_POSITION_TYPES type)
{
    return type == X_POSITION_TYPE_LONG;
}

//
bool IsXBearish(ENUM_POSITION_TYPE type)
{
    return type == POSITION_TYPE_SELL;
}

//
bool IsXBearish(ENUM_X_POSITION_TYPES type)
{
    return type == X_POSITION_TYPE_SHORT;
}

//
// Ages ...

//
// Retrieve a Position Age ...
int GetAge(
    XSignal &signal,              // Selected Signal ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    //
    int result = -1;

    //
    if (period == NULL)
    {
        period = _Period;
    }

    //
    result = iBarShift(
        signal.symbol,
        period,
        signal.time);

    //
    return result;
}
int GetXAge(
    XSignal &signal,              // Selected Signal ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    return GetAge(signal, period);
}

//
// Retrieve a Position Age ...
int GetAge(
    XPosition &position,          // Selected Position ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    //
    int result = -1;

    //
    if (period == NULL)
    {
        period = _Period;
    }

    //
    result = iBarShift(
        position.symbol,
        period,
        position.openAt);

    //
    return result;
}
int GetXAge(
    XPosition &position,          // Selected Position ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    return GetAge(position, period);
}

//
// Retrieve an Order Age ...
int GetAge(
    XOrder &order,                // Selected Order ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    //
    int result = -1;

    //
    if (period == NULL)
    {
        period = _Period;
    }

    //
    result = iBarShift(
        order.symbol,
        period,
        order.setupAt);

    //
    return result;
}
int GetXAge(
    XOrder &order,                // Selected Order ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    return GetAge(order, period);
}

//
// Retrieve a Deal Age ...
int GetAge(
    XDeal &deal,                  // Selected Deal ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    //
    int result = -1;

    //
    if (period == NULL)
    {
        period = _Period;
    }

    //
    result = iBarShift(
        deal.symbol,
        period,
        deal.time);

    //
    return result;
}
int GetXAge(
    XDeal &deal,                  // Selected Deal ...
    ENUM_TIMEFRAMES period = NULL // Dest Time Frame ...
)
{
    return GetAge(deal, period);
}

//
// Get Oldest Deal ...
int GetOldest(
    XDeal &item,   // Hold Result ...
    XDeal &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XDeal tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XDeal iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge > 0 &&
            (tmpAge == 0 || tmpAge < iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge > 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXOldest(
    XDeal &item,   // Hold Result ...
    XDeal &items[] // Source Array ...
)
{
    return GetOldest(item, items);
}

//
// Get Youngest Deal ...
int GetYoungest(
    XDeal &item,   // Hold Result ...
    XDeal &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XDeal tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XDeal iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge > 0 &&
            (tmpAge == 0 || tmpAge > iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge > 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXYoungest(
    XDeal &item,   // Hold Result ...
    XDeal &items[] // Source Array ...
)
{
    return GetYoungest(item, items);
}

//
// Get Oldest Order ...
int GetOldest(
    XOrder &item,   // Hold Result ...
    XOrder &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XOrder tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XOrder iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge > 0 &&
            (tmpAge == 0 || tmpAge < iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge > 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXOldest(
    XOrder &item,   // Hold Result ...
    XOrder &items[] // Source Array ...
)
{
    return GetOldest(item, items);
}

//
// Get Youngest Order ...
int GetYoungest(
    XOrder &item,   // Hold Result ...
    XOrder &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XOrder tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XOrder iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge > 0 &&
            (tmpAge == 0 || tmpAge > iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge > 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXYoungest(
    XOrder &item,   // Hold Result ...
    XOrder &items[] // Source Array ...
)
{
    return GetYoungest(item, items);
}

//
// Get Oldes Position ...
int GetOldest(
    XPosition &item,   // Hold Result ...
    XPosition &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XPosition tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XPosition iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge > 0 &&
            (tmpAge == 0 || tmpAge < iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge > 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXOldest(
    XPosition &item,   // Hold Result ...
    XPosition &items[] // Source Array ...
)
{
    return GetOldest(item, items);
}

//
// Get Youngest Position ...
int GetYoungest(
    XPosition &item,   // Hold Result ...
    XPosition &items[] // Source Array ...
)
{
    //
    int result = -1;

    //
    int itemsCount = ArraySize(items);
    if (itemsCount <= 0)
    {
        return result;
    }

    //
    int tmpAge = 0;
    XPosition tmpItem;
    for (int i = 0; i < itemsCount; i++)
    {
        //
        XPosition iItem = items[i];

        //
        int iAge = GetAge(iItem);
        if (iAge >= 0 &&
            (tmpAge == 0 || tmpAge > iAge))
        {
            //
            tmpAge = iAge;
            tmpItem = iItem;
        }
    }

    //
    if (tmpAge >= 0)
    {
        //
        result = tmpAge;
        item = tmpItem;
    }

    //
    return result;
}
int GetXYoungest(
    XPosition &item,   // Hold Result ...
    XPosition &items[] // Source Array ...
)
{
    return GetYoungest(item, items);
}

//
int FindMaxProfitIndex(
    XPosition &positions[] // Position Collection
)
{
    //
    int result = -1;

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    double max = 0;
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];
        if (iPosition.profit <= 0)
        {
            continue;
        }

        //
        if (max == 0 ||
            max < iPosition.profit)
        {
            //
            result = i;
            max = iPosition.profit;
        }
    }

    //
    return result;
}

//
int FindMinProfitIndex(
    XPosition &positions[] // Position Collection
)
{
    //
    int result = -1;

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    double min = 0;
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];
        if (iPosition.profit <= 0)
        {
            continue;
        }

        //
        if (min == 0 ||
            min > iPosition.profit)
        {
            //
            result = i;
            min = iPosition.profit;
        }
    }

    //
    return result;
}

//
int ExtractInProfitPositions(
    XPosition &positions[], // Position Collection
    XPosition &inProfits[]  // Result
)
{
    //
    int result = 0;

    //
    Clean(inProfits);

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        if (iPosition.profit > 0)
        {
            //
            AddRef(
                iPosition,
                inProfits //
            );
        }
    }

    //
    result = ArraySize(inProfits);

    //
    return result;
}

//
int FindMaxDrawdownIndex(
    XPosition &positions[] // Position Collection
)
{
    //
    int result = -1;

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    double max = 0;
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];
        if (iPosition.profit >= 0)
        {
            continue;
        }

        //
        if (max == 0 ||
            MathAbs(max) < MathAbs(iPosition.profit))
        {
            //
            result = i;
            max = iPosition.profit;
        }
    }

    //
    return result;
}

//
int FindMinDrawdownIndex(
    XPosition &positions[] // Position Collection
)
{
    //
    int result = -1;

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    double min = 0;
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];
        if (iPosition.profit >= 0)
        {
            continue;
        }

        //
        if (min == 0 ||
            min > iPosition.profit)
        {
            //
            result = i;
            min = iPosition.profit;
        }
    }

    //
    return result;
}

//
int ExtractInDrawdownPositions(
    XPosition &positions[], // Position Collection
    XPosition &inDradowns[] // Result
)
{
    //
    int result = 0;

    //
    Clean(inDradowns);

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        if (iPosition.profit < 0)
        {
            //
            AddRef(
                iPosition,
                inDradowns //
            );
        }
    }

    //
    result = ArraySize(inDradowns);

    //
    return result;
}

//
// Check Filters ...

//
// Magic Filter ...
template <typename T>
bool IsMagicFilterPassed(
    ulong magic,
    bool filterByMagic,
    T &item
    //
)
{
    //
    return !filterByMagic
               ? true
           : !NotEmpty(magic)
               ? false
               : magic == item.magic;
}

//
// Symbol Filter ...
template <typename T>
bool IsSymbolFilterPassed(
    string symbol,
    T &item
    //
)
{
    //
    return !IsValid(symbol)
               ? true
               : symbol == item.symbol;
}

//
// Provider Filter ...
template <typename T>
bool IsProviderFilterPassed(
    string provider,
    T &item
    //
)
{
    //
    return !IsValid(provider)
               ? true
               : provider == item.provider ||
                     Contains(provider, item.provider);
}

//
// Period Filter ...
template <typename T>
bool IsPeriodFilterPassed(
    ENUM_TIMEFRAMES period,
    T &item
    //
)
{
    //
    return !IsValid(period)
               ? true
               : period == item.period;
}

//
// Type Filter ...
template <typename T>
bool IsTypeFilterPassed(
    ENUM_POSITION_TYPE type,
    T &item
    //
)
{
    //
    bool result = type == item.type;

    //
    return result;
}
template <typename T>
bool IsTypeFilterPassed(
    ENUM_X_POSITION_TYPES type,
    T &item
    //
)
{
    //
    bool result = type == NULL || type == X_POSITION_TYPE_ALL
                      ? true
                      : ((type == X_POSITION_TYPE_LONG && ((int)item.type == (int)POSITION_TYPE_BUY || (int)item.type == (int)ORDER_TYPE_BUY)) ||
                         (type == X_POSITION_TYPE_SHORT && ((int)item.type == (int)POSITION_TYPE_SELL || (int)item.type == (int)ORDER_TYPE_SELL)));

    //
    return result;
}
template <typename T>
bool IsTypeFilterPassed(
    ENUM_ORDER_TYPE type,
    T &item
    //
)
{
    //
    return type == NULL
               ? true
               : type == item.type;
}
template <typename T>
bool IsTypeFilterPassed(
    ENUM_DEAL_TYPE type,
    T &item
    //
)
{
    //
    return type == NULL
               ? true
               : type == item.type;
}

//
// Mode Filter ...
template <typename T>
bool IsModeFilterPassed(
    ENUM_X_ORDER_MODES mode,
    T &item
    //
)
{
    //
    return mode == NULL
               ? true
               : mode == item.mode;
}

//
// Entry Filter ...
template <typename T>
bool IsEntryFilterPassed(
    ENUM_DEAL_ENTRY entry,
    T &item
    //
)
{
    //
    return entry == NULL
               ? true
               : entry == item.entry;
}

//
// Reason Filter ...
template <typename T>
bool IsReasonFilterPassed(
    ENUM_DEAL_REASON reason,
    T &item
    //
)
{
    //
    return reason == NULL
               ? true
               : reason == item.reason;
}

//
// State Filter ...
template <typename T>
bool IsStateFilterPassed(
    ENUM_ORDER_STATE state,
    T &item
    //
)
{
    //
    return state == NULL
               ? true
               : state == item.state;
}

//

//
//
// Calculate Positions Profit Summary ...
double SpecifiedCalculatePositionsProfit(
    XPosition &positions[] // Source
)
{
    //
    double result = 0;

    //
    int positionsCount = ArraySize(positions);
    if (positionsCount <= 0)
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        result += iPosition.profit;
    }

    //
    return result;
}

//
// Calculate Required Profit for Hedging ...
double SpecifiedCalculateRequiredProfitForHedge(
    XPosition &positions[], // Source
    double mMinProfitPerTrade,
    double mMinProfitPerVolumeFactor //
)
{
    //
    double result = 0;

    //
    int positionsCount = ArraySize(positions);
    if (positionsCount <= 0)
    {
        return result;
    }

    //
    bool isHedgingEnable = mMinProfitPerTrade > 0 && mMinProfitPerVolumeFactor > 0;
    if (!isHedgingEnable)
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        if (mMinProfitPerTrade > 0 && mMinProfitPerVolumeFactor > 0)
        {
            //
            result +=
                ((iPosition.volume / mMinProfitPerVolumeFactor) *
                 mMinProfitPerTrade) +
                (-1 * iPosition.swap);
        }
    }

    //
    return result;
}

//
bool SpecifiedIsPositionsReadyForHedge(
    XPosition &positions[], // Source
    double mMinProfitPerTrade,
    double mMinProfitPerVolumeFactor //
)
{
    //
    double profit = SpecifiedCalculatePositionsProfit(positions);
    double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
        positions,
        mMinProfitPerTrade,
        mMinProfitPerVolumeFactor //
    );

    //
    bool result = requiredProfit > 0
                      ? profit >= requiredProfit
                      : profit > requiredProfit;

    //
    return result;
}

//
int FindCoverageDrawdownPosition(
    XPosition &position,    // In Drawdown Position
    XPosition &inProfits[], // In Profit Positions
    XPosition &coverage[],  // Coverage Positons
    double mMinProfitPerTrade,
    double mMinProfitPerVolumeFactor //
)
{
    //
    int result = 0;

    //
    Clean(coverage);

    //
    if (!position.IsValid() || position.profit >= 0)
    {
        return result;
    }

    //
    int inProfitsCount = ArraySize(inProfits);
    if (!IsValidSize(inProfitsCount))
    {
        return result;
    }

    //
    // Copy in Profits to TMP ...
    XPosition tmp[];
    Copy(
        inProfits,
        tmp //
    );

    //
    bool isCoverPassed = false;
    while (!isCoverPassed || ArraySize(tmp) > 0)
    {
        //
        XPosition max;
        int idx = FindMaxProfitIndex(tmp);
        if (idx >= 0)
        {
            //
            if (tmp[idx].profit > 0)
            {
                AddRef(
                    tmp[idx],
                    coverage //
                );

                //
                ArrayRemove(
                    tmp,
                    idx,
                    1 //
                );
            }
        }

        //
        // Check Cover Passed ...
        XPosition tmpPositions[];
        Copy(
            coverage,
            tmpPositions //
        );
        AddRef(
            position,
            tmpPositions //
        );

        //
        double profits = SpecifiedCalculatePositionsProfit(tmpPositions);
        double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
            tmpPositions,
            mMinProfitPerTrade,
            mMinProfitPerVolumeFactor //
        );
        isCoverPassed = profits >= requiredProfit;
        if (isCoverPassed)
        {
            break;
        }

        //
        if (!isCoverPassed && ArraySize(tmp) == 0)
        {
            break;
        }
    }

    //
    result = ArraySize(coverage);

    //
    return result;
}

//
// Extract a Collection of Positions SL ...
int ExtractSLs(
    XPosition &positions[],
    double &sls[] //
)
{
    //
    int result = 0;

    //
    Clean(sls);

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        if (iPosition.sl > 0)
        {
            //
            Add(
                iPosition.sl,
                sls //
            );
        }
    }

    //
    result = ArraySize(sls);

    //
    return result;
}

//
// Extract a Colletion of Positions TP ...
int ExtractTPs(
    XPosition &positions[],
    double &tps[] //
)
{
    //
    int result = 0;

    //
    Clean(tps);

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return result;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        if (iPosition.tp > 0)
        {
            //
            Add(
                iPosition.tp,
                tps //
            );
        }
    }

    //
    result = ArraySize(tps);

    //
    return result;
}

//
// Retrieve String Representation ...
string ToString(ENUM_X_SIGNAL_EXECUTION_RESULT value)
{
    //
    string result = NULL;

    //
    switch (value)
    {
    //
    case X_SIGNAL_EXECUTION_UNKNOWN:
        result = "Unknown";
        break;

    //
    case X_SIGNAL_EXECUTION_SUCCEED:
        result = "Succed";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_SPREAD:
        result = "Spread more than Max Allowed";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED:
        result = "not Allowed";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_NO_EQUITY:
        result = "No Equity for Trade";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_PRICE_ERROR:
        result = "Price Error";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_PREV_PROFIT_ERROR:
        result = "Previous Position Profit not Passed for Accept Next";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_MAX_REACHED:
        result = "Max Allowed Positions Reached";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED:
        result = "Min Delay Between to Signal not Passed";
        break;

    //
    case X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS:
        result = "Invalid Params";
        break;
    }

    //
    return result;
}

//
// Converts Position Type to Order Type ...
ENUM_ORDER_TYPE ToOrderType(ENUM_POSITION_TYPE value)
{
    //
    bool isLong = IsLong(value);

    //
    ENUM_ORDER_TYPE result =
        isLong
            ? ORDER_TYPE_BUY
            : ORDER_TYPE_SELL;

    //
    return result;
}
ENUM_ORDER_TYPE ToOrderType(ENUM_X_POSITION_TYPES value)
{
    //
    ENUM_POSITION_TYPE mType =
        value == X_POSITION_TYPE_SHORT
            ? POSITION_TYPE_SELL
            : POSITION_TYPE_BUY;

    //
    bool isLong = IsLong(mType);

    //
    ENUM_ORDER_TYPE result =
        isLong
            ? ORDER_TYPE_BUY
            : ORDER_TYPE_SELL;

    //
    return result;
}

//
// Tagging ...

//
// Generate Specified Tag for Using in Comments ...
string GeneratePeriodTag(ENUM_TIMEFRAMES period)
{
    //
    string strPeriod = ToXString(period);
    return Surround(
        XPeriodToken,
        strPeriod
        //
    );
}

//
// Extract Period from a Tagged string ...
ENUM_TIMEFRAMES ExtractPeriod(string value)
{
    //
    ENUM_TIMEFRAMES result = NULL;

    //
    string pStr = ParseStringSurrounded(
        value,
        XPeriodToken
        //
    );
    if (!IsValid(pStr))
    {
        return result;
    }

    //
    result = ToPeriod(pStr);

    //
    return result;
}

// Generate Specified Tag for Using in Comments ...
string GenerateProviderTag(string provider)
{
    //
    return Surround(
        XProviderToken,
        provider
        //
    );
}

//
// Extract Provider from a Tagged string ...
string ExtractProvider(string value)
{
    //
    return ParseStringSurrounded(
        value,
        XProviderToken
        //
    );
}

//
// Generate Support Comment Tag ...
string GenerateSupportTag(ulong ticket)
{
    //
    return Surround(
        XSupportToken,
        ticket
        //
    );
}

//
// Extract Support Positions from a Tagged string ...
ulong ExtractSupportedTicket(string value)
{
    //
    return ParseLongSurrounded(
        value,
        XSupportToken
        //
    );
}

//
// Extract Support ...
template <typename T>
int ExtractSupports(
    T &source[],
    T &dest[] // Result ...
)
{
    //
    int result = 0;

    //
    Clean(dest);

    //
    int sourceCount = ArraySize(source);
    if (!IsValidSize(sourceCount))
    {
        return result;
    }

    //
    for (int i = 0; i < sourceCount; i++)
    {
        //
        T iSource = source[i];

        //
        ulong isSupport = ExtractSupportedTicket(iSource.comment) > 0;
        ulong iEQMSupport = ExtractEQMSupportedTicket(iSource.comment) > 0;
        if (isSupport || iEQMSupport)
        {
            //
            AddRef(
                iSource,
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

//
// Generate EQM Support Comment Tag ...
string GenerateEQMSupportTag(ulong ticket)
{
    //
    return Surround(
        XEQMSupportToken,
        ticket
        //
    );
}

//
// Extract EQM Support Positions from a Tagged string ...
ulong ExtractEQMSupportedTicket(string value)
{
    //
    return ParseLongSurrounded(
        value,
        XEQMSupportToken
        //
    );
}

//
bool IsSupport(string value)
{
    //
    bool result = false;

    //
    bool isSupport = ExtractSupportedTicket(value) > 0;
    bool isEQMSupport = ExtractEQMSupportedTicket(value) > 0;

    //
    result =
        //
        isSupport
        //
        ||
        //
        isEQMSupport
        //
        ;

    //
    return result;
}

//
ulong ExtractParentTicket(string value)
{
    //
    ulong result = 0;

    //
    if (!IsSupport(value))
    {
        return result;
    }

    //
    result = ExtractSupportedTicket(value);
    if (result > 0)
    {
        return result;
    }

    //
    result = ExtractEQMSupportedTicket(value);

    //
    return result;
}

//
// XRecovery ...

//
string GenerateRecoveryTag(ulong ticket)
{
    //
    return Surround(
        XRecoveryToken,
        ticket //
    );
}

//
ulong ExtractRecoveredTicket(string value)
{
    //
    return ParseLongSurrounded(
        value,
        XRecoveryToken //
    );
}

//
// Extract Recoveries ...
template <typename T>
int ExtractRecoveries(
    T &source[],
    T &dest[] // Result ...
)
{
    //
    int result = 0;

    //
    Clean(dest);

    //
    int sourceCount = ArraySize(source);
    if (!IsValidSize(sourceCount))
    {
        return result;
    }

    //
    for (int i = 0; i < sourceCount; i++)
    {
        //
        T iSource = source[i];

        //
        ulong isRecovery = ExtractRecoveredTicket(iSource.comment) > 0;
        if (isRecovery)
        {
            //
            AddRef(
                iSource,
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

//
// Extract Specified Position's Recoveries ...
int ExtractRecoveries(
    ulong ticket,
    XPosition &source[],
    XPosition &dest[] // Result ...
)
{
    //
    int result = 0;

    //
    Clean(dest);

    //
    int sourceCount = ArraySize(source);
    if (!IsValidSize(sourceCount))
    {
        return result;
    }

    //
    for (int i = 0; i < sourceCount; i++)
    {
        //
        XPosition iSource = source[i];

        //
        ulong parentTicket = ExtractRecoveredTicket(iSource.comment);
        bool isRecovery = parentTicket > 0;
        if (isRecovery && parentTicket == ticket)
        {
            //
            AddRef(
                iSource,
                dest //
            );
        }
    }

    //
    result = ArraySize(dest);

    //
    return result;
}

//
// SL Trails ...

//
// Extract SL Trail Level ...
int ExtractSLTrailLevel(string comment)
{
    //
    int result = 0;

    //
    if (!IsValid(comment))
    {
        return result;
    }

    //
    result = ParseIntSurrounded(
        comment,
        XSLTrailToken
        //
    );

    //
    return result;
}

//
// Generate SL Trail Comment Tag ...
string GenerateSLTrailTag(int level)
{
    //
    string result = NULL;

    //
    if (level <= 0)
    {
        return result;
    }

    //
    result = Surround(
        XSLTrailToken,
        level
        //
    );

    //
    return result;
}

//
// Prepare SL Trail Comment ...
string PrepareSLTrailTag(
    const string comment // Original Position Comment ...
)
{
    //
    string result = NULL;

    //
    int level = 0;
    result = comment;

    //
    // Check Comment Contains SL Trailing or not ...
    int lastTrailLevel = ExtractSLTrailLevel(comment);
    if (lastTrailLevel <= 0)
    {
        level++;
    }
    else
    {
        //
        level = lastTrailLevel + 1;
        string lastLevelStr = GenerateSLTrailTag(lastTrailLevel);
        StringReplace(
            result,
            lastLevelStr,
            ""
            //
        );
    }

    //
    // Generate level Comment ...
    string levelStr = GenerateSLTrailTag(level);
    result += levelStr;

    //
    return result;
}

//
// Count Positions from a Collection ...
void CountPositions(
    const XPosition &positions[],
    int &longs,
    double &longProfits,
    double &longVolumes, //
    //
    int &shorts,
    double &shortProfits,
    double &shortVolumes //
)
{
    //
    longs = 0;
    longProfits = 0;
    longVolumes = 0;

    //
    shorts = 0;
    shortProfits = 0;
    shortVolumes = 0;

    //
    int positionsCount = ArraySize(positions);
    if (!IsValidSize(positionsCount))
    {
        return;
    }

    //
    for (int i = 0; i < positionsCount; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        bool isLong = IsLong(iPosition.type);
        if (isLong)
        {
            //
            longs++;
            longProfits += iPosition.profit;
            longVolumes += iPosition.volume;
        }
        else
        {
            //
            shorts++;
            shortProfits += iPosition.profit;
            shortVolumes += iPosition.volume;
        }
    }
}

//
// Extract Positions based On Type ...
void ExtractPositions(
    XPosition &positions[],
    XPosition &longs[],
    XPosition &shorts[] //
)
{
    //
    int count = ArraySize(positions);
    if (!IsValidSize(count))
    {
        return;
    }

    //
    Clean(longs);
    Clean(shorts);

    //
    for (int i = 0; i < count; i++)
    {
        //
        XPosition iPosition = positions[i];

        //
        bool isLong = IsLong(iPosition.type);
        if (isLong)
        {
            AddRef(
                iPosition,
                longs //
            );
        }
        else
        {
            AddRef(
                iPosition,
                shorts //
            );
        }
    }
}

//
// Apply a Collection of Targets
// on Specified Signal ...
void ApplyTargetsOnSignal(
    ENUM_X_DIRECTION sortingDir, // Target Sorting Direction ...
    XTarget &targets[],          // Required Targets ...
    XSignal &signal              // Destination Signal ...
)
{
    //
    bool has =
        HasChild(targets) &&
        HasDirection(sortingDir);
    if (!has)
    {
        return;
    }

    //
    // Sort Targets ...
    ApplySortOnTargets(
        sortingDir,
        targets //
    );

    //
    // Compare TP with Targets ...
    int idx = IsLong(signal.type)
                  ? GetHighest(targets)
                  : GetLowest(targets);
    has = IsValidIndex(idx);
    if (has)
    {
        //
        // Compare TP ...
        has = IsLong(signal.type)
                  ? targets[idx].target > signal.tp
                  : targets[idx].target < signal.tp;
        if (has)
        {
            //
            XTarget tpTarget;
            tpTarget = targets[idx];
            tpTarget.actingDistance = 0;
            tpTarget.target = signal.tp;

            //
            signal.tp = targets[idx].target;
            targets[idx].actingDistance = 0;

            //
            ArrayRemove(
                targets,
                idx,
                1 //
            );

            //
            AddRef(
                tpTarget,
                targets //
            );
        }

        //
        // Sort Targets ...
        ApplySortOnTargets(
            sortingDir,
            targets //
        );
    }

    //
    Copy(
        targets,
        signal.targets //
    );
}

/**
 * Select Specified Position's Related Position(s)
 * for Protecting ...
 *
 * @param selecteds: XPosition instance reference Collection, for Holding Selected Positions ...
 * @param positions: XPosition instance reference Collection, All available source Positions ...
 * @param forType: ENUM_X_POSITION_TYPES member, Spcified Position Type to Select for it ...
 * @param selectType: ENUM_XPOSITION_SELECT_TYPES member, Specified Positions Selecting Senario ...
 *
 * @return ( bool )
 */
bool SelectPosition(
    XPosition &selecteds[],
    XPosition &positions[],
    ENUM_X_POSITION_TYPES forType,
    ENUM_XPOSITION_SELECT_TYPES selectType //
)
{
    //
    bool result = false;

    //
    // Normalize Args ...
    Clean(selecteds);

    //
    // Validate Args ...
    result =
        IsValid(forType) &&
        HasChild(positions) &&
        IsXValid(selectType);
    if (!result)
    {
        return result;
    }

    //
    // Extract Separate Type of Positions ...

    //
    int idx = -1;
    bool has = false;
    XPosition iPosition;

    //
    bool isLong = IsLong(forType);
    bool isShort = IsShort(forType);
    int count = ArraySize(positions);

    //
    XPosition samePositions[];
    XPosition oppositPositions[];
    XPosition inProfitPositions[];
    XPosition inDrawdownPositions[];
    XPosition sameInProfitPositions[];
    XPosition sameInDrawdownPositions[];
    XPosition oppositInProfitPositions[];
    XPosition oppositInDrawdownPositions[];

    //
    for (int i = 0; i < count; i++)
    {
        //
        // Select Indexed Position ...
        iPosition = positions[i];

        //
        bool isIPosLong = IsLong(iPosition.type);
        bool isIPosShort = IsShort(iPosition.type);

        //
        // Select if Same Type ...
        bool isSame =
            (isLong && isIPosLong) ||
            (isShort && isIPosShort);
        if (isSame)
        {
            //
            AddRef(
                iPosition,
                samePositions //
            );
        }

        //
        // Select if Opposit Type ...
        bool isOpposit =
            (isLong && isIPosShort) ||
            (isShort && isIPosLong);
        if (isOpposit)
        {
            //
            AddRef(
                iPosition,
                oppositPositions //
            );
        }

        //
        iPosition.Clean();
    }

    //
    // Filling Other Requirement Collections ...

    //
    ExtractInProfitPositions(
        positions,
        inProfitPositions //
    );

    //
    ExtractInProfitPositions(
        samePositions,
        sameInProfitPositions //
    );

    //
    ExtractInProfitPositions(
        oppositPositions,
        oppositInProfitPositions //
    );

    //
    ExtractInDrawdownPositions(
        positions,
        inDrawdownPositions //
    );

    //
    ExtractInDrawdownPositions(
        samePositions,
        sameInDrawdownPositions //
    );

    //
    ExtractInDrawdownPositions(
        oppositPositions,
        oppositInDrawdownPositions //
    );

    //
    // Implement Selection Senario ...
    switch (selectType)
    {
    //
    case XPOSITION_SELECT_ALL:
        //
        Copy(
            positions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_IN_PROFITS:
        //
        Copy(
            inProfitPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_IN_DRAWDOWNS:
        //
        Copy(
            inDrawdownPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_OLDEST:
        //
        GetOldest(
            iPosition,
            positions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            positions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_PROFITS_OLDEST:
        //
        GetOldest(
            iPosition,
            inProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_PROFITS_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            inProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_PROFITS_MIN:
        //
        idx = FindMinProfitIndex(inProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = inProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_PROFITS_MAX:
        //
        idx = FindMaxProfitIndex(inProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = inProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_DRAWDOWN_OLDEST:
        //
        GetOldest(
            iPosition,
            inDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_DRAWDOWN_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            inDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_DRAWDOWN_MIN:
        //
        idx = FindMinDrawdownIndex(inDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = inDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_IN_DRAWDOWN_MAX:
        //
        idx = FindMaxDrawdownIndex(inDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = inDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAMES:
        //
        Copy(
            samePositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_SAMES_OLDEST:
        //
        GetOldest(
            iPosition,
            samePositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAMES_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            samePositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSITS:
        //
        Copy(
            oppositPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_OLDEST:
        //
        GetOldest(
            iPosition,
            oppositPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            oppositPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_PROFITS:
        //
        Copy(
            sameInProfitPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_SAME_IN_PROFITS_OLDEST:
        //
        GetOldest(
            iPosition,
            sameInProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_PROFITS_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            sameInProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_PROFITS_MIN:
        //
        idx = FindMinProfitIndex(sameInProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = sameInProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_PROFITS_MAX:
        //
        idx = FindMaxProfitIndex(sameInProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = sameInProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_IN_PROFITS:
        //
        Copy(
            oppositInProfitPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_IN_PROFITS_OLDEST:
        //
        GetOldest(
            iPosition,
            oppositInProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_IN_PROFITS_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            oppositInProfitPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_IN_PROFITS_MIN:
        //
        idx = FindMinProfitIndex(oppositInProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = oppositInProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSIT_IN_PROFITS_MAX:
        //
        idx = FindMaxProfitIndex(oppositInProfitPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = oppositInProfitPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_DRAWDOWNS:
        //
        Copy(
            sameInDrawdownPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_SAME_IN_DRAWDOWN_OLDEST:
        //
        GetOldest(
            iPosition,
            sameInDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_DRAWDOWN_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            sameInDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_DRAWDOWN_MIN:
        //
        idx = FindMinDrawdownIndex(sameInDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = sameInDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_SAME_IN_DRAWDOWN_MAX:
        //
        idx = FindMaxDrawdownIndex(sameInDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = sameInDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWNS:
        //
        Copy(
            oppositInDrawdownPositions,
            selecteds //
        );
        break;

    //
    case XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_OLDEST:
        //
        GetOldest(
            iPosition,
            oppositInDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_YOUNGEST:
        //
        GetYoungest(
            iPosition,
            oppositInDrawdownPositions //
        );
        if (iPosition.IsValid())
        {
            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_MIN:
        //
        idx = FindMinDrawdownIndex(oppositInDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = oppositInDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

    //
    case XPOSITION_SELECT_OPPOSITS_IN_DRAWDOWN_MAX:
        //
        idx = FindMaxDrawdownIndex(oppositInDrawdownPositions);
        if (IsValidIndex(idx))
        {
            //
            iPosition = oppositInDrawdownPositions[idx];

            //
            AddRef(
                iPosition,
                selecteds //
            );
        }
        iPosition.Clean();
        break;

        //
    }

    //
    // Validate Result ...
    result = HasChild(selecteds);

    //
    // Cleanup Resources ...

    //
    if (!result)
    {
        Clean(selecteds);
    }

    //
    Clean(samePositions);
    Clean(oppositPositions);
    Clean(inProfitPositions);
    Clean(inDrawdownPositions);
    Clean(sameInProfitPositions);
    Clean(sameInDrawdownPositions);
    Clean(oppositInProfitPositions);
    Clean(oppositInDrawdownPositions);

    //
    return result;
}

//
