diff --git a/Classes/x-saherelm.x-market.pattern.class.mq5 b/Classes/x-saherelm.x-market.pattern.class.mq5 new file mode 100644 index 00000000..17a2cf85 --- /dev/null +++ b/Classes/x-saherelm.x-market.pattern.class.mq5 @@ -0,0 +1,1069 @@ +/////////////////////////////////////////////////////// +// +// 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" + +// +// Definitions ... + +// +// Implementation ... +class XCMarketPatternDetector : public XCBase +{ + // + // Public ... + public: + // + // Props ... + + // + // Constructor ... + XCMarketPatternDetector() + { + } + + // + // Deconstructor ... + ~XCMarketPatternDetector() + { + } + + // + // Actions / Helpers ... + + /** + * 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: int, a Multipler for approve Momentum ... + * + * @return ( bool ) + */ + bool IsMomentumBar( + XOHCL &bar, + ENUM_X_DIRECTION &dir, + int requiredBars = 3, + int approvedMultiplier = 2 // + ) + { + // + bool result = false; + + // + // Prepare ... + CleanDirection(dir); + + // + // Normalizing ... + requiredBars = NormalizeInt(requiredBars, 3, 10); + approvedMultiplier = NormalizeInt(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: int, 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, + int shadowApprovedMultiplier = 3, + double minimumPinBarRangeMultiplier = 4, + bool forceDirection = false // + ) + { + // + bool result = false; + + // + // Prepare ... + CleanDirection(dir); + + // + // Normalizing ... + shadowApprovedMultiplier = NormalizeInt(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 IsStartPattern( + 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 ... + * @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; + } + + bool HasTrend( + 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 ... + + + // + iSwing.Clean(); + SpecifiedClean(swingLows); + SpecifiedClean(swingHighs); + + // + return result; + } + + // + // Protected ... + protected: + // + + // + // Private ... + private: + // + + // + // Helpers ... + + /** + * Clean Specified Referenced Direction ... + * + * @param dir: ENUM_X_DIRECTION + */ + void CleanDirection(ENUM_X_DIRECTION &dir) + { + dir = X_DIRECTION_NONE; + } +}; + +// \ No newline at end of file diff --git a/Experts/x-saherelm.xfima.backtester.ea.mq5 b/Experts/x-saherelm.xfima.backtester.ea.mq5 index b758b426..2febc5af 100644 --- a/Experts/x-saherelm.xfima.backtester.ea.mq5 +++ b/Experts/x-saherelm.xfima.backtester.ea.mq5 @@ -24,6 +24,7 @@ // // Imports ... #include "../Classes/x-saherelm.x-backtester.expert.class.mq5" +#include "../Classes/x-saherelm.x-market.pattern.class.mq5" #include "../Helpers/x-saherelm.xom1.helper.mq5" // @@ -120,8 +121,12 @@ input int valeArrowCode = 159; // Vales Arrow Code // Define Local Variables ... // -string eaLogTag; // EA Log Prefix ... -XCBackTesterEA *eaExpert; // EA Expert ... +string eaLogTag; // EA Log Prefix ... +XCBackTesterEA *eaExpert; // EA Expert ... +XCMarketPatternDetector *eaPatternDetector; // EA Pattern Detector ... + +// +XBarTracker eaBarTracker; // EA Bar Traker ... // int eaHandler = INVALID_HANDLE; // Indicator Handler ... @@ -284,6 +289,23 @@ int OnInit() return INIT_FAILED; } + // + // Initialize Bar Tracker ... + isInited = eaBarTracker.Init( + _Symbol, + _Period // + ); + if (!isInited) + { + // + Print(ShortName, ": Initialization Failed ..."); + return INIT_FAILED; + } + + // + // Instantiate Market Pattern Detector ... + eaPatternDetector = new XCMarketPatternDetector(); + // Print(ShortName, ": Initialized Successfully ..."); return INIT_SUCCEEDED; @@ -313,6 +335,13 @@ void OnDeinit(const int reason) // De Init Indicator ... IndicatorRelease(eaHandler); + // + eaBarTracker.Clean(); + + // + delete eaPatternDetector; + ZeroMemory(eaPatternDetector); + // delete eaExpert; ZeroMemory(eaExpert); @@ -322,7 +351,156 @@ void OnDeinit(const int reason) // On Tick Handler ... void OnTick() { + // eaExpert.HandleOnTick(); + + // + bool isNewBar = eaBarTracker.IsNewBar(); + if (isNewBar) + { + // + // Initialize 1 Indexed Bar ... + XOHCL cBar; + bool has = cBar.Init( + _Symbol, + _Period, + 1 // + ); + if (has) + { + // + // Do Analysis ... + + // + ENUM_X_DIRECTION iDir = X_DIRECTION_NONE; + + // + // Checking Momentum Bar ... + bool isMomentum = eaPatternDetector.IsMomentumBar( + cBar, + iDir // + ); + bool isBullishMomentum = isMomentum && + IsSpecifiedBullish(iDir); + bool isBearishMomentum = isMomentum && + IsSpecifiedBearish(iDir); + if (isMomentum) + { + Print(ToXString(iDir), " Momentum Bar: ", cBar.time); + } + + // + // Checking Engulfed Bar ... + bool isEngulfed = eaPatternDetector.IsEngulfedBar( + cBar, + iDir // + ); + bool isBullishEngulfed = isEngulfed && + IsSpecifiedBullish(iDir); + bool isBearishEngulfed = isEngulfed && + IsSpecifiedBearish(iDir); + if (isEngulfed) + { + Print(ToXString(iDir), " Engulfed Bar: ", cBar.time); + } + + // + // Checking TruGaped Bar ... + bool isTrueGaped = eaPatternDetector.IsTrueGapedBar( + cBar, + iDir // + ); + bool isBullishTrueGaped = isTrueGaped && + IsSpecifiedBullish(iDir); + bool isBearishTrueGaped = isTrueGaped && + IsSpecifiedBearish(iDir); + if (isTrueGaped) + { + Print(ToXString(iDir), " TrueGapped Bar: ", cBar.time); + } + + // + // Checking Pinned Bar ... + bool isPinned = eaPatternDetector.IsPinnedBar( + cBar, + iDir // + ); + bool isBullishPinned = isPinned && + IsSpecifiedBullish(iDir); + bool isBearishPinned = isPinned && + IsSpecifiedBearish(iDir); + if (isPinned) + { + Print(ToXString(iDir), " Pinned Bar: ", cBar.time); + } + + // + // Checking Star Pattern ... + bool isStarPattern = eaPatternDetector.IsStartPattern( + cBar, + iDir // + ); + bool isBullishStarPattern = isStarPattern && + IsSpecifiedBullish(iDir); + bool isBearishStarPattern = isStarPattern && + IsSpecifiedBearish(iDir); + if (isStarPattern) + { + Print(ToXString(iDir), " Star Pattern: ", cBar.time); + } + + // + // Checking Piercing Pattern ... + bool isPiercingPattern = eaPatternDetector.IsPiercingPattern( + cBar, + iDir // + ); + bool isBullishPiercingPattern = isPiercingPattern && + IsSpecifiedBullish(iDir); + bool isBearishPiercingPattern = isPiercingPattern && + IsSpecifiedBearish(iDir); + if (isPiercingPattern) + { + Print(ToXString(iDir), " Piercing Pattern: ", cBar.time); + } + + // + // Checking Rising Pattern ... + bool isRisingPattern = eaPatternDetector.IsRisingPattern( + cBar, + iDir // + ); + bool isBullishRisingPattern = isRisingPattern && + IsSpecifiedBullish(iDir); + bool isBearishRisingPattern = isRisingPattern && + IsSpecifiedBearish(iDir); + if (isRisingPattern) + { + Print(ToXString(iDir), " Rising Pattern: ", cBar.time); + } + + // + // Checking Flag Pattern ... + XOHCL swingBar; + bool isFlagPattern = eaPatternDetector.IsFlagPattern( + cBar, + iDir, + swingBar // + ); + bool isBullishFlagPattern = isFlagPattern && + IsSpecifiedBullish(iDir); + bool isBearishFlagPattern = isFlagPattern && + IsSpecifiedBearish(iDir); + if (isFlagPattern) + { + Print(ToXString(iDir), " Flag Pattern: ", cBar.time, ", Swing: ", swingBar.time); + } + swingBar.Clean(); + } + + // + cBar.Clean(); + } } // diff --git a/Libraries/x-saherelm.common.extensions.lib.mq5 b/Libraries/x-saherelm.common.extensions.lib.mq5 index b92136b9..021cc8d7 100644 --- a/Libraries/x-saherelm.common.extensions.lib.mq5 +++ b/Libraries/x-saherelm.common.extensions.lib.mq5 @@ -862,9 +862,9 @@ double GetXSpread( /** * Get SPecified Symbols Volume Step ... - * + * * @param mSymbol: string ... - * + * * @return ( double ) */ double GetVolumeStep( @@ -1110,6 +1110,116 @@ bool GetTick( 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 mode = ToSeriesMode(mType); + result = + mode == MODE_HIGH + ? iHighest( + mSymbol, + mPeriod, + mode, + mShoulders, + mFromIndex // + ) + : iLowest( + mSymbol, + mPeriod, + mode, + mShoulders, + mFromIndex // + ); + + // + return result; +} + // // END Position ... // diff --git a/Libraries/x-saherelm.x-enums.lib.mq5 b/Libraries/x-saherelm.x-enums.lib.mq5 index a0403e37..3997e94f 100644 --- a/Libraries/x-saherelm.x-enums.lib.mq5 +++ b/Libraries/x-saherelm.x-enums.lib.mq5 @@ -747,6 +747,10 @@ bool IsValid(ENUM_X_SWING_TYPE value) // return result; } +bool IsXValid(ENUM_X_SWING_TYPE value) +{ + return IsValid(value); +} /** * Converts To String ... diff --git a/Libraries/x-saherelm.x-ohcl.lib.mq5 b/Libraries/x-saherelm.x-ohcl.lib.mq5 index d212e212..b6540834 100644 --- a/Libraries/x-saherelm.x-ohcl.lib.mq5 +++ b/Libraries/x-saherelm.x-ohcl.lib.mq5 @@ -1339,6 +1339,171 @@ struct XOHCL 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; + } + // // Other ... string GetTag(string prefix = "")