From 3b31c36541c79af1836b0b328dad8379d026bcb1 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sat, 15 Mar 2025 04:13:08 +0330 Subject: [PATCH] last works on implementing Candlestick Patterns on Bar Analyser Class ... --- Classes/x-saherelm.x-bar.analyser.class.mq5 | 590 +++++++++++++++++- Documents/BKP/tmp.bar.anal.mq5 | 46 -- Experts/x-saherelm.xpoi.test.ea.mq5 | 121 +++- .../x-saherelm.common.extensions.lib.mq5 | 308 +++++++++ 4 files changed, 979 insertions(+), 86 deletions(-) diff --git a/Classes/x-saherelm.x-bar.analyser.class.mq5 b/Classes/x-saherelm.x-bar.analyser.class.mq5 index 8ef12212..725310c5 100644 --- a/Classes/x-saherelm.x-bar.analyser.class.mq5 +++ b/Classes/x-saherelm.x-bar.analyser.class.mq5 @@ -26,9 +26,6 @@ // // Definitions ... -// -#define X_PERCENTAGE_GOLDEN_MULTIPLIER 100000 - // // Implementations ... class XCBarAnalyser : public XCBase @@ -423,7 +420,7 @@ class XCBarAnalyser : public XCBase for (int i = 0; i < uppersCount; i++) { // - double iValue = uppers[i] - lowers[i]; + double iValue = lowers[i] + (uppers[i] - lowers[i]); // Add( @@ -707,45 +704,48 @@ class XCBarAnalyser : public XCBase return result; } - // - // Actions ... - - bool IsSpike( + /** + * Detect Big Price Change Happens or not ... + * + * @param bar: XOHCL instance reference, Start Bar ... + * @param dir: ENUM_X_DIRECTION member, Event Direction ... + * @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 IsBigChange( XOHCL &bar, - XOHCL &fromBar, - XOHCL &toBar, ENUM_X_DIRECTION &dir, - double minPriceChangePercent = 1.5, ENUM_X_PRICE type = X_PRICE_CLOSE, bool forcBodyInRange = true, - double rangeExtendMultiplier = 2, - int loopback = 14 // + double rangeExtendMultiplier = 1.0 // ) { // bool result = false; // - toBar.Clean(); - fromBar.Clean(); dir = X_DIRECTION_NONE; - loopback = NormalizeInt(loopback, 1); rangeExtendMultiplier = NormalizeDouble(rangeExtendMultiplier, 1); - minPriceChangePercent = NormalizeDouble(minPriceChangePercent, 1.5); // - int periodSeconds = PeriodSeconds(bar.period); - double divider = X_PERCENTAGE_GOLDEN_MULTIPLIER; - double periodMult = (periodSeconds / PeriodSeconds(PERIOD_M5)) * 0.2; - minPriceChangePercent /= (divider / periodMult); - - // - result = bar.IsValid(); + result = + bar.IsValid() && + IsValid(type); if (!result) { return result; } + // + double minPriceChangePercent = 0; + int loopback = GetPeriodSharpData( + minPriceChangePercent, + bar.period // + ); + // double ranges[]; int rangesCount = GetPriceRange( @@ -765,6 +765,7 @@ class XCBarAnalyser : public XCBase } // + // Calculate Ranges ... double rangeMax = GetMax(ranges); double rangeMin = GetMin(ranges); double rangeAverage = GetAverage(ranges); @@ -775,29 +776,54 @@ class XCBarAnalyser : public XCBase ); // - static double maxPriceChange = 0; - static double minPriceChange = 0; - if (maxPriceChange == 0 || maxPriceChange < priceChangePercent) + // 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) { - maxPriceChange = priceChangePercent; - } - if (minPriceChange == 0 || minPriceChange > priceChangePercent) - { - minPriceChange = priceChangePercent; + return result; } + // + int hhDiff = (hhIDX - bar.Index()); + int llDiff = (llIDX - bar.Index()); + // bool isBullish = // + hhDiff <= 1 && + llIDX > hhIDX && + bar.close >= hh && priceChangePercent > 0 && - priceChangePercent >= minPriceChangePercent + priceChangePercent >= minPriceChangePercent && + bar.high > (rangeAverage * rangeExtendMultiplier) // ; // bool isBearish = // + llDiff <= 1 && + hhIDX > llIDX && + bar.close <= ll && priceChangePercent < 0 && + bar.low < (rangeAverage * rangeExtendMultiplier) && MathAbs(priceChangePercent) >= minPriceChangePercent // ; @@ -823,6 +849,504 @@ class XCBarAnalyser : public XCBase return result; } + // + // Actions ... + + /** + * Detect Spike ... + * + * @param bar: XOHCL instance reference, Start Bar ... + * @param dir: ENUM_X_DIRECTION member, Event Direction ... + * @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 ... + * @param sameBars: int, Specified How Many Same Bars Required for an Spike ... + * + * @return ( bool ) + */ + bool IsSpike( + XOHCL &bar, + ENUM_X_DIRECTION &dir, + ENUM_X_PRICE type = X_PRICE_CLOSE, + bool forcBodyInRange = true, + double rangeExtendMultiplier = 1.0, + int sameBars = 3 // + ) + { + // + bool result = false; + + // + dir = X_DIRECTION_NONE; + sameBars = NormalizeInt(sameBars, 2); + + // + result = IsValid(type) && + bar.IsValid(); + if (!result) + { + return result; + } + + // + // First We Have to Has a Big Change ... + result = IsBigChange( + bar, + dir, + type, + forcBodyInRange, + rangeExtendMultiplier // + ); + if (!result) + { + return result; + } + + // + // Check Has Same Bars ... + int sameBarsIndex = -1; + ENUM_X_DIRECTION sameBarsDir; + result = HasSameBars( + bar, + sameBarsDir, + sameBarsIndex, + sameBars // + ); + if (!result) + { + return result; + } + + // + // Check Same Bars Direction sames To Price Change ... + result = sameBarsDir == dir; + if (!result) + { + 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 Doji 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 Hammer Pattern or not ... + * + * @param bar: XOHCL instance reference, Specified Bar to Check ... + * @param dir: ENUM_X_DIRECTION member, Specified Doji 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 Doji 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 Doji Direction ... + * @param loopback: int, Specified Loopback ... + * @param rangeExtendMultiplier: double, a Multiplier for Checking Price Range ... + * + * @return ( bool ) + */ + bool IsMomentum( + XOHCL &bar, + ENUM_X_DIRECTION &dir, + int loopback = 2, + double rangeExtendMultiplier = 1.5 // + ) + { + // + bool result = false; + + // + dir = X_DIRECTION_NONE; + loopback = NormalizeInt(loopback, 1); + rangeExtendMultiplier = NormalizeDouble(rangeExtendMultiplier, 1); + + // + 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.low > ll && + IsBullish(dir) && + bar.close >= hh; + + // + bool isBearish = + bar.high < hh && + IsBearish(dir) && + bar.close <= ll; + + // + result = isBullish || + isBearish; + if (result) + { + // + dir = + isBullish + ? X_DIRECTION_BULLISH + : X_DIRECTION_BEARISH; + } + + // + // Cleanup Resources ... + + // + 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 Doji 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; + } + // protected: // diff --git a/Documents/BKP/tmp.bar.anal.mq5 b/Documents/BKP/tmp.bar.anal.mq5 index 46a05f52..e02f03c3 100644 --- a/Documents/BKP/tmp.bar.anal.mq5 +++ b/Documents/BKP/tmp.bar.anal.mq5 @@ -2988,52 +2988,6 @@ // // Decisions ... - // - bool IsBullishDecision( - XOHCL &bar, - double percent = 70 // Percent of Body - ) - { - // - bool result = false; - - // - result = - // - bar.IsValid() && - bar.Index() > 0 && - bar.IsBullish() && - (bar.GetRange() * percent) / 100 <= bar.GetBody() - // - ; - - // - return result; - } - - // - bool IsBearishDecision( - XOHCL &bar, - double percent = 70 // Percent of Body - ) - { - // - bool result = false; - - // - result = - // - bar.IsValid() && - bar.Index() > 0 && - bar.IsBearish() && - (bar.GetRange() * percent) / 100 <= bar.GetBody() - // - ; - - // - return result; - } - // // Candlestic Patterns ... diff --git a/Experts/x-saherelm.xpoi.test.ea.mq5 b/Experts/x-saherelm.xpoi.test.ea.mq5 index af5285ee..00fc5262 100644 --- a/Experts/x-saherelm.xpoi.test.ea.mq5 +++ b/Experts/x-saherelm.xpoi.test.ea.mq5 @@ -24,6 +24,7 @@ // // XCPOIDetector *mPOIDetector; +XBarTracker mBarTracker; XCBarAnalyser *mBarAnalyser; // @@ -47,6 +48,12 @@ int OnInit() // mBarAnalyser = new XCBarAnalyser(); + // + mBarTracker.Init( + _Symbol, + _Period // + ); + // return (INIT_SUCCEEDED); } @@ -89,6 +96,12 @@ void DetectBarAnalyser() return; } + // + if (!mBarTracker.IsNewBar()) + { + return; + } + // XOHCL zBar; bool isInited = zBar.Init( @@ -96,21 +109,115 @@ void DetectBarAnalyser() _Period, 0 // ); + if (!isInited) + { + // + zBar.Clean(); + return; + } // - XOHCL toBar; - XOHCL fromBar; + XOHCL cBar; + isInited = zBar.GetPreviousBar(cBar); + if (!isInited) + { + // + zBar.Clean(); + cBar.Clean(); + return; + } + + // + // Check Big Change in Price ... ENUM_X_DIRECTION dir; + bool isBigChange = mBarAnalyser.IsBigChange( + zBar, // Bar ... + dir, // Change Direction ... + X_PRICE_CLOSE, // Price Change Type ... + true, // Force Use Body for Range Calculation ... + 1.0 // Range Extend Multiplier ... + ); + if (isBigChange) + { + // Print("Big Price Change Detected: ", ToString(dir)); + } + + // + // Check Big Change in Price ... bool isSpike = mBarAnalyser.IsSpike( - zBar, - fromBar, - toBar, - dir // + zBar, // Bar ... + dir, // Change Direction ... + X_PRICE_CLOSE, // Price Change Type ... + true, // Force Use Body for Range Calculation ... + 1.0, // Range Extend Multiplier ... + 3 // Same Bars ... ); if (isSpike) { - Print("Spike Detected ..."); + // Print("Spike Detected: ", ToString(dir)); } + + // + // Check Doji Pattern ... + bool isDoji = mBarAnalyser.IsDoji( + cBar, + dir // + ); + if (isDoji) + { + Print(ToString(dir), " Doji Detected ..."); + } + + // + // Check Hammer Pattern ... + bool isHammer = mBarAnalyser.IsHammer( + cBar, + dir // + ); + if (isHammer) + { + Print(ToString(dir), " Hammer Detected ..."); + } + + // + // Check Engulf Pattern ... + bool isEngulfed = mBarAnalyser.IsEngulfed( + cBar, + dir // + ); + if (isEngulfed) + { + Print(ToString(dir), " Engulfed Detected ..."); + } + + // + // Check Momentum Pattern ... + bool isMomentum = mBarAnalyser.IsMomentum( + cBar, + dir // + ); + if (isMomentum) + { + Print(ToString(dir), " Momentum Detected ..."); + } + + // + // Check Pullback Pattern ... + bool isPullback = mBarAnalyser.IsPullback( + cBar, + dir // + ); + if (isMomentum) + { + Print(ToString(dir), " Pullback Detected ..."); + } + + // + // Cleanup Resources ... + + // + zBar.Clean(); + cBar.Clean(); } /** diff --git a/Libraries/x-saherelm.common.extensions.lib.mq5 b/Libraries/x-saherelm.common.extensions.lib.mq5 index 2eba7f47..37c5c47d 100644 --- a/Libraries/x-saherelm.common.extensions.lib.mq5 +++ b/Libraries/x-saherelm.common.extensions.lib.mq5 @@ -3425,6 +3425,183 @@ int GetCycleLengthOn( 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.000008; + result = 14; + break; + + // + case PERIOD_M2: + // + priceChange = 0.000008; + result = 14; + break; + + // + case PERIOD_M3: + // + priceChange = 0.00001; + result = 14; + break; + + // + case PERIOD_M4: + // + priceChange = 0.000011; + result = 14; + break; + + // + case PERIOD_M5: + // + priceChange = 0.000015; + result = 14; + break; + + // + case PERIOD_M6: + // + priceChange = 0.000015; + result = 14; + break; + + // + case PERIOD_M10: + // + priceChange = 0.000015; + result = 12; + break; + + // + case PERIOD_M12: + // + priceChange = 0.000015; + result = 11; + break; + + // + case PERIOD_M15: + // + priceChange = 0.000015; + result = 9; + break; + + // + case PERIOD_M20: + // + priceChange = 0.000015; + result = 9; + break; + + // + case PERIOD_M30: + // + priceChange = 0.000017; + result = 7; + break; + + // + case PERIOD_H1: + // + priceChange = 0.000019; + result = 5; + break; + + // + case PERIOD_H2: + // + priceChange = 0.000019; + result = 5; + break; + + // + case PERIOD_H3: + // + priceChange = 0.000019; + result = 5; + break; + + // + case PERIOD_H4: + // + priceChange = 0.000022; + result = 5; + break; + + // + case PERIOD_H6: + // + priceChange = 0.000022; + result = 5; + break; + + // + case PERIOD_H8: + // + priceChange = 0.000025; + result = 5; + break; + + // + case PERIOD_H12: + // + priceChange = 0.000027; + result = 5; + break; + + // + case PERIOD_D1: + // + priceChange = 0.000027; + result = 3; + break; + + // + case PERIOD_W1: + // + priceChange = 0.00003; + result = 3; + break; + + // + case PERIOD_MN1: + // + priceChange = 0.00003; + result = 3; + break; + } + + // + return result; +} + // // END Cycles ... // @@ -5873,6 +6050,137 @@ double GetAppliedPrice( 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; +} + // // END Price ... //