From f5d341af5722269ac1809e557e69efe20578c785 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sat, 12 Oct 2024 22:35:26 +0330 Subject: [PATCH] adding zone ... --- Classes/x-saherelm.zone.class.mq5 | 79 +++ Indicators/x-saherelm.xchvp.mq5 | 1011 +++++++++++++++++++++++++++++ 2 files changed, 1090 insertions(+) create mode 100644 Classes/x-saherelm.zone.class.mq5 create mode 100644 Indicators/x-saherelm.xchvp.mq5 diff --git a/Classes/x-saherelm.zone.class.mq5 b/Classes/x-saherelm.zone.class.mq5 new file mode 100644 index 00000000..5951af81 --- /dev/null +++ b/Classes/x-saherelm.zone.class.mq5 @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Class Library +// ---------------------------------------------- +// Name: XSCZone +// Description: provides all Zone 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 +#include "../Libraries/x-saherelm.common.lib.mq5" + +// +// Specific Zone ... +class XSCZone : public CObject +{ + // + // Public ... +public: + // + // Props ... + double high; + double low; + int ticks; + double percent; + + // + ENUM_TIMEFRAMES period; + datetime start; + datetime end; + + // + // Compare Two Zone With Each Other ... + virtual int Compare( + const CObject *node, + const int mode = 0 // + ) const + { + // + const XSCZone *other = (XSCZone *)node; + + // + return other.ticks - ticks; + } + + // + // Props ... + string GetTag() { + // + string result = ""; + + // + result = "XZ_" + ToString(period) + "_" + ToString(start); + + // + return result; + } + + // + // Protected ... +protected: + // + // Private ... +private: +}; diff --git a/Indicators/x-saherelm.xchvp.mq5 b/Indicators/x-saherelm.xchvp.mq5 new file mode 100644 index 00000000..20d69077 --- /dev/null +++ b/Indicators/x-saherelm.xchvp.mq5 @@ -0,0 +1,1011 @@ +/////////////////////////////////////////////////////// +// 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; +} + +// \ No newline at end of file