/////////////////////////////////////////////////////// // SaherElm IT Center MQL5 Indicator // ------------------------------------------- // Name: XCHVP // Description: XCHVP Cycles ... // // 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 XCHVP Indicator" #property strict // #define ShortName "XCHVP" // // Includes Common Library ... #include "../Classes/x-saherelm.zone.class.mq5" #include "../Libraries/x-saherelm.draw.lib.mq5" // // Inputs ... // input group "Market"; input int numberOfLevels = 10; // Number of Levels in Each Zone input int numberOfZones = 2; // Number of Zones in Each Cycle // input group "Cycles"; // input group "Short"; input ENUM_X_PERIOD_METHOD scMethod = X_PERIOD_AUTO; // How to Find Period input ENUM_TIMEFRAMES scPeriod = NULL; // Time Period // input group "Medium"; input ENUM_X_PERIOD_METHOD mcMethod = X_PERIOD_AUTO; // How to Find Period input ENUM_TIMEFRAMES mcPeriod = NULL; // Time Period // input group "Long"; input ENUM_X_PERIOD_METHOD lcMethod = X_PERIOD_AUTO; // How to Find Period input ENUM_TIMEFRAMES lcPeriod = NULL; // Time Period // input group "Hind"; input ENUM_X_PERIOD_METHOD hcMethod = X_PERIOD_AUTO; // How to Find Period input ENUM_TIMEFRAMES hcPeriod = NULL; // Time Period // // Presentation ... input group "Presentation"; // input bool showShort = true; // Show Short Cycle input bool showMedium = true; // Show Medium Cycle input bool showLong = true; // Show Long Cycle input bool showHind = true; // Show Hind Cycle // input color shortColor = clrAqua; // Short CycleColor input color mediumColor = clrMagenta; // Medium Cycle Color input color longColor = clrSeaGreen; // Long Cycle Color input color hindColor = clrYellow; // Hind Cycle Color // // Buffers ... // #property indicator_chart_window // #property indicator_buffers 0 #property indicator_plots 0 // // Variables, Properties and etc ... // int maxLength; // // Current ... // // Short ... int mSCycleLength = 0; ENUM_TIMEFRAMES mSCPeriod = NULL; double mSLow; double mSHigh; double mSLows[]; double mSHighs[]; MqlTick mSTicks[]; CArrayObj mSZones; datetime mSLowTime; datetime mSHighTime; datetime mSLastCalculateTime = NULL; // // Medium ... int mMCycleLength = 0; ENUM_TIMEFRAMES mMCPeriod = NULL; double mMLow; double mMHigh; double mMLows[]; double mMHighs[]; MqlTick mMTicks[]; CArrayObj mMZones; datetime mMLowTime; datetime mMHighTime; datetime mMLastCalculateTime = NULL; // // Long ... int mLCycleLength = 0; ENUM_TIMEFRAMES mLCPeriod = NULL; double mLLow; double mLHigh; double mLLows[]; double mLHighs[]; MqlTick mLTicks[]; CArrayObj mLZones; datetime mLLowTime; datetime mLHighTime; datetime mLLastCalculateTime = NULL; // // Hind ... int mHCycleLength = 0; ENUM_TIMEFRAMES mHCPeriod = NULL; double mHLow; double mHHigh; double mHLows[]; double mHHighs[]; MqlTick mHTicks[]; CArrayObj mHZones; datetime mHLowTime; datetime mHHighTime; datetime mHLastCalculateTime = NULL; // // Event Handlers ... // // Initialization ... int OnInit() { // // Validate Inputs ... if (!ValidateInputs()) { return INIT_PARAMETERS_INCORRECT; } // if (!InitMarketCycles()) { return INIT_PARAMETERS_INCORRECT; } // // 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(); // drawPrefix = ShortName; // string comment = // "Short: " + ToString(mSCPeriod) + "\n" + "Medium: " + ToString(mMCPeriod) + "\n" + "Long: " + ToString(mLCPeriod) + "\n" + "Hind: " + ToString(mHCPeriod) + "\n" // ; Comment(comment); // // 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 // Comment(""); RemoveDraws(); } // // 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--) { // // Print("BarIndex: ", i); // CalculateBuffers( // i, prev_calculated, rates_total, // open, high, close, low, tick_volume // ); } // return rates_total; } // // Functions ... // // Validate Input Args for Initialization ... bool ValidateInputs() { // bool result = false; // result = // numberOfLevels > 0 && numberOfZones > 0 && (IsValid(scMethod, scPeriod) && IsValid(mcMethod, mcPeriod) && IsValid(lcMethod, lcPeriod) && IsValid(hcMethod, hcPeriod)) // ; // return result; } // // Initialize Market Cycles ... bool InitMarketCycles() { // bool result = false; // // Current Cycle Initialization ... // int cPeriodSeconds = PeriodSeconds(_Period); // // Short Cycle Initialization ... // // Find Cycle Period ... if (scMethod == X_PERIOD_AUTO) { // // Select Period ... mSCPeriod = GetCyclePeriod( X_MARKET_CYCLE_SHORT, _Period // ); } else { mSCPeriod = scPeriod; } // result = IsValid(mSCPeriod); if (!result) { return result; } // mSCycleLength = PeriodSeconds(mSCPeriod) / cPeriodSeconds; result = mSCycleLength > 0; if (!result) { return result; } // // Medium Cycle Initialization ... // // Find Cycle Period ... if (mcMethod == X_PERIOD_AUTO) { // // Select Period ... mMCPeriod = GetCyclePeriod( X_MARKET_CYCLE_MEDIUM, _Period // ); } else { mMCPeriod = mcPeriod; } // result = IsValid(mMCPeriod); if (!result) { return result; } // mMCycleLength = PeriodSeconds(mMCPeriod) / cPeriodSeconds; result = mMCycleLength > 0; if (!result) { return result; } // // Long Cycle Initialization ... // // Find Cycle Period ... if (lcMethod == X_PERIOD_AUTO) { // // Select Period ... mLCPeriod = GetCyclePeriod( X_MARKET_CYCLE_LONG, _Period // ); } else { mLCPeriod = lcPeriod; } // result = IsValid(mLCPeriod); if (!result) { return result; } // mLCycleLength = PeriodSeconds(mLCPeriod) / cPeriodSeconds; result = mLCycleLength > 0; if (!result) { return result; } // // Hind Cycle Initialization ... // // Find Cycle Period ... if (hcMethod == X_PERIOD_AUTO) { // // Select Period ... mHCPeriod = GetCyclePeriod( X_MARKET_CYCLE_HIND, _Period // ); } else { mHCPeriod = hcPeriod; } // result = IsValid(mHCPeriod); if (!result) { return result; } // mHCycleLength = PeriodSeconds(mHCPeriod) / cPeriodSeconds; result = mHCycleLength > 0; if (!result) { return result; } // return result; } // // Retrieve all Exists Input Max Length ... // use for Start Of Drawing ... int ExtractMaxLengthOfInputs() { // int result = 0; // result = MathMax(mSCycleLength, mMCycleLength); result = MathMax(result, mLCycleLength); result = MathMax(result, mHCycleLength); // return result; } // // Set Indicator Short Name and also we can define Buffers Labels ... void SetIndicatorName() { IndicatorSetString(INDICATOR_SHORTNAME, ShortName); } // // Define Indexes and Styles ... void DefineBuffers() { } // // Calculations ... // // Calculate Buffers ... void CalculateBuffers( int bar_index, // Selected Bar Index const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // CalculateCycles( bar_index, prevCalculated, ratesTotal, open, high, close, low, tickVolume // ); } // void CalculateCycles( int bar_index, // Selected Bar Index const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // // SHORT ... CalculateCycle( bar_index, prevCalculated, ratesTotal, open, high, close, low, tickVolume, // mSCPeriod, mSCycleLength, mSLastCalculateTime, mSTicks, mSZones, // mSHigh, mSHighTime, mSHighs, // mSLow, mSLowTime, mSLows, // showShort, shortColor // ); // // MEDIUM ... CalculateCycle( bar_index, prevCalculated, ratesTotal, open, high, close, low, tickVolume, // mMCPeriod, mMCycleLength, mMLastCalculateTime, mMTicks, mMZones, // mMHigh, mMHighTime, mMHighs, // mMLow, mMLowTime, mMLows, // showMedium, mediumColor // ); // // LONG ... CalculateCycle( bar_index, prevCalculated, ratesTotal, open, high, close, low, tickVolume, // mLCPeriod, mLCycleLength, mLLastCalculateTime, mLTicks, mLZones, // mLHigh, mLHighTime, mLHighs, // mLLow, mLLowTime, mLLows, // showLong, longColor // ); // // HIND ... CalculateCycle( bar_index, prevCalculated, ratesTotal, open, high, close, low, tickVolume, // mHCPeriod, mHCycleLength, mHLastCalculateTime, mHTicks, mHZones, // mHHigh, mHHighTime, mHHighs, // mHLow, mHLowTime, mHLows, // showHind, hindColor // ); } // void CalculateCycle( int bar_index, // Selected Bar Index const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[], // ENUM_TIMEFRAMES period, int cycleLength, datetime &lastCalculatedTime, MqlTick &ticks[], CArrayObj &pZones, // double &pHigh, datetime &pHighTime, double &highs[], // double &pLow, datetime &pLowTime, double &lows[], // bool canDraw, color drawColor // ) { // // Calculate Reuired Data ... // int maxRequiredLength = ExtractMaxLengthOfInputs(); int requiredItemsLength = (numberOfZones) * maxRequiredLength; if (bar_index > requiredItemsLength) { return; } // datetime bar_time = iTime( _Symbol, _Period, bar_index // ); if (IsValid(lastCalculatedTime) && lastCalculatedTime >= bar_time) { lastCalculatedTime = NULL; return; } // datetime periodStartTime = GetPeriodStartTime( _Symbol, period, bar_time // ); // // Retrieve Ticks between Times ... Clean(ticks); int copedTicks = CopyTicksRange( _Symbol, ticks, COPY_TICKS_ALL, periodStartTime * 1000, bar_time * 1000 // ); if (copedTicks <= 0) { return; } // // Retrieve Highs ... Clean(highs); int copiedHighs = CopyHigh( _Symbol, period, periodStartTime, bar_time, highs // ); if (copiedHighs <= 0) { return; } // // Retrieve Highs ... Clean(lows); int copiedLows = CopyLow( _Symbol, period, periodStartTime, bar_time, lows // ); if (copiedLows <= 0) { return; } // datetime mTimes[]; int copiedTimes = CopyTime( _Symbol, period, periodStartTime, bar_time, mTimes // ); if (copiedTimes <= 0) { return; } // int highIDX = ArrayMaximum(highs); pHigh = highs[highIDX]; // int lowIDX = ArrayMinimum(lows); pLow = lows[lowIDX]; // pHighTime = mTimes[highIDX]; pLowTime = mTimes[lowIDX]; // double rangeSize = pHigh - pLow; // // Create Zones ... pZones.Clear(); for (int i = 0; i < numberOfLevels; i++) { // // Instance a New Zone ... XSCZone *iZone = new XSCZone(); iZone.end = bar_time; iZone.period = period; iZone.start = periodStartTime; // // Calculate Each Zones High / Low based on Range ... double iH = pHigh - rangeSize * i / numberOfLevels; double iL = pHigh - rangeSize * (i + 1) / numberOfLevels; // // Set Zone High Low Properties ... iZone.low = iL; iZone.high = iH; // // Add Zone to Zone Array Objects ... pZones.Add(iZone); } // // Calculate Each Zone Ticks ... int ticksCount = ArraySize(ticks); for (int i = 0; i < ticksCount; i++) { // // Retrieve i Index Tick Object ... MqlTick iTick = ticks[i]; // // Loop Through Zones ... for (int j = 0; j < pZones.Total(); j++) { // // Retrieve j Index Zone Object ... XSCZone *jZone = pZones.At(j); // bool isInRange = IsTickInZoneRange( iTick, jZone // ); if (isInRange) { // jZone.ticks++; break; } } } // // Calculating Zone Percents ... for (int i = 0; i < pZones.Total(); i++) { // XSCZone *iZone = pZones.At(i); // double percent = CalculateZoneTickPercent(ticks, iZone); iZone.percent = percent; } // pZones.Sort(); // // Draw Cycle ... if (canDraw) { DrawCycleZones(pZones, drawColor); } // // Print("----------------"); // Print("BarTime: ", bar_time); // Print("PEriodStartTime: ", periodStartTime); // Print("Period: ", ToString(period)); // Print("Length: ", cycleLength); // lastCalculatedTime = bar_time; } // // Draw Specific Cycles Zone ... void DrawCycleZones( CArrayObj &zones, color clr // ) { // int count = zones.Total(); if (!IsValidSize(count)) { return; } // // Sort Zones ... zones.Sort(); // string tag = NULL; int maxZoneIDX = -1; XSCZone *maxZone = NULL; for (int i = 0; i < count; i++) { // XSCZone *iZone = zones.At(i); // if (!IsValid(tag)) { tag = iZone.GetTag(); } // if (maxZone == NULL || maxZone.ticks < iZone.ticks) { // maxZone = iZone; maxZoneIDX = i; } // // Draw Start Vertical Line ... string startVLName = tag + "_" + "Start"; DrawVerticalLine( ChartID(), startVLName, 0, iZone.start, clr, STYLE_SOLID, 1 // ); } // // Draw Levels Rectangle ... string levelRectName = tag + "_" + ToString(maxZoneIDX) + "_Level"; DrawRectangle( ChartID(), levelRectName, 0, maxZone.start, maxZone.high, maxZone.end, maxZone.low, clr, STYLE_SOLID, 1 // ); } // // Check a Tick is in a Zone rage or not ... bool IsTickInZoneRange( MqlTick &tick, // Tick For Checking XSCZone *zone // Zone For Checking ) { // bool result = false; // result = // tick.bid >= zone.low && tick.bid <= zone.high // ; // return result; } // double CalculateZoneTickPercent( MqlTick &ticks[], XSCZone *zone, int normalizationDigits = 2 // ) { // double result = 0; // if (normalizationDigits < 2 || normalizationDigits > 5) { normalizationDigits = 2; } // int ticksCount = ArraySize(ticks); if (ticksCount <= 0) { return result; } // result = (double)zone.ticks / ticksCount * 100; result = NormalizeDouble(result, normalizationDigits); // return result; } //