From bf8b74d4e7d3746304754579a4b7716b25e11d49 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Wed, 19 Feb 2025 03:17:38 +0330 Subject: [PATCH] Add support for Draw Trend Line for Order Flow ... --- Classes/x-saherelm.x-poi.drawer.class.mq5 | 1 + Libraries/x-saherelm.x-poi.lib.mq5 | 208 ++++++ .../Signals/x-121.smc.xwz.signal.class.mq5 | 228 +++++- .../Signals/x-121.smc.xwz.signal.lib.mq5 | 680 +++++------------- 4 files changed, 613 insertions(+), 504 deletions(-) diff --git a/Classes/x-saherelm.x-poi.drawer.class.mq5 b/Classes/x-saherelm.x-poi.drawer.class.mq5 index 1a9fd719..8954063a 100644 --- a/Classes/x-saherelm.x-poi.drawer.class.mq5 +++ b/Classes/x-saherelm.x-poi.drawer.class.mq5 @@ -4332,6 +4332,7 @@ class XCPOIDrawer : public XCBase // int DrawOBFVG( + // XOBFVG &item, XCBaseObject *&objects[], // diff --git a/Libraries/x-saherelm.x-poi.lib.mq5 b/Libraries/x-saherelm.x-poi.lib.mq5 index eb7ee1ff..1a8c2323 100644 --- a/Libraries/x-saherelm.x-poi.lib.mq5 +++ b/Libraries/x-saherelm.x-poi.lib.mq5 @@ -6067,6 +6067,51 @@ struct XBoxZone } }; +// +struct XOBFVGPivot +{ + // + // Props ... + double value; + datetime at; + + // + // Constructor ... + XOBFVGPivot() + { + Clean(); + } + + // + // Tools ... + + // + void Clean() + { + // + value = 0; + at = NULL; + + // + ZeroMemory(this); + } + + // + bool IsValid() + { + // + bool result = false; + + // + result = + value > 0 && + IsValid(at); + + // + return result; + } +}; + // struct XOBFVG { @@ -6105,6 +6150,9 @@ struct XOBFVG Clean(demandZones); Clean(supportZones); Clean(resistanceZones); + + // + ZeroMemory(this); } // @@ -6176,6 +6224,166 @@ struct XOBFVG return ArraySize(resistanceZones); } + // + ENUM_X_DIRECTION GetDirection() + { + // + ENUM_X_DIRECTION result = X_DIRECTION_NONE; + + // + bool isValid = IsValid(); + if (!isValid) + { + return result; + } + + // + result = ob.GetDirection(); + + // + return result; + } + + // + bool IsBullish() + { + return IsBullish(GetDirection()); + } + + // + bool IsBearish() + { + return IsBearish(GetDirection()); + } + + // + int GetSwings( + XOBFVGPivot &pivots[] // + ) + { + // + int result = 0; + + // + Clean(pivots); + + // + if (!IsValid()) + { + return result; + } + + // + ENUM_X_DIRECTION forDir = GetDirection(); + bool isBullish = IsBullish(forDir); + + // + XOHCL passedBar; + XOBFVGPivot iPivot; + bool isPassed = false; + double appliedPrice = 0; + bool isBodyState = false; + + // + // Start ... + isPassed = ob.IsStarted( + forDir, + passedBar, + isBodyState // + ); + if (isPassed) + { + // + appliedPrice = + isBullish + ? passedBar.low + : passedBar.high; + + // + iPivot.Clean(); + iPivot.value = appliedPrice; + iPivot.at = passedBar.time; + + // + AddRef( + iPivot, + pivots // + ); + + // + iPivot.Clean(); + } + + // + // Activation ... + isPassed = ob.IsActivated( + forDir, + passedBar, + isBodyState // + ); + if (isPassed) + { + // + appliedPrice = + isBullish + ? passedBar.low + : passedBar.high; + + // + iPivot.Clean(); + iPivot.value = appliedPrice; + iPivot.at = passedBar.time; + + // + AddRef( + iPivot, + pivots // + ); + + // + iPivot.Clean(); + } + + // + // Worked ... + isPassed = ob.IsWorked( + forDir, + passedBar, + isBodyState // + ); + if (isPassed) + { + // + appliedPrice = + isBullish + ? passedBar.low + : passedBar.high; + + // + iPivot.Clean(); + iPivot.value = appliedPrice; + iPivot.at = passedBar.time; + + // + AddRef( + iPivot, + pivots // + ); + + // + iPivot.Clean(); + } + + // + passedBar.Clean(); + + // + result = ArraySize(pivots); + + // + return result; + } + // void UpdateTo(datetime value = NULL) { diff --git a/X121SMCEA/Signals/x-121.smc.xwz.signal.class.mq5 b/X121SMCEA/Signals/x-121.smc.xwz.signal.class.mq5 index 78408ffb..7796c6ca 100644 --- a/X121SMCEA/Signals/x-121.smc.xwz.signal.class.mq5 +++ b/X121SMCEA/Signals/x-121.smc.xwz.signal.class.mq5 @@ -232,26 +232,47 @@ class XCX121SMCXWZSignalDetector : public XCBase // Setup Conditions ... // - int bullishFlowCount = mConditions.DetectOrderFlow( - X_DIRECTION_BULLISH, - consolidationState, - analyseState, - decisionState, - zIndex, - period, + ENUM_X_DIRECTION trend = X_DIRECTION_NONE; + bool hasOrderFlow = mConditions.HasOrderFlow(trend); + if (!hasOrderFlow) + { + // + int orderFlowCount = mConditions.DetectOrderFlow( + trend, + consolidationState, + analyseState, + decisionState // + ); + hasOrderFlow = IsValidSize(orderFlowCount); + } + + // + result = hasOrderFlow; + if (!result) + { + // + // TODO: Implement Clean ... + + // + return result; + } + + // + isBullish = IsBullish(trend); + isBearish = IsBearish(trend); + + // + // Has Order Flow ... + + // + // TODO: Remove this ... + DrawConditions( + mConditions, visionCycleHelper.mPOIDrawer // ); // - int bearishFlowCount = mConditions.DetectOrderFlow( - X_DIRECTION_BEARISH, - consolidationState, - analyseState, - decisionState, - zIndex, - period, - visionCycleHelper.mPOIDrawer // - ); + // return false; @@ -1535,6 +1556,7 @@ class XCX121SMCXWZSignalDetector : public XCBase void DrawConditions( X121SMCStrategyXWZSignalConditions &conditions, XCPOIDrawer *drawer, + bool drawTrendLine = true, bool forceClean = false // ) { @@ -1551,6 +1573,8 @@ class XCX121SMCXWZSignalDetector : public XCBase } // + int count = 0; + bool has = false; bool isCreated = false; // @@ -1558,6 +1582,160 @@ class XCX121SMCXWZSignalDetector : public XCBase int subWindow = drawer.SubWindowIdentification(); color bullishColor = drawer.BarBullishColor(); color bearishColor = drawer.BarBearishColor(); + + // + ENUM_X_DIRECTION trend; + has = conditions.HasOrderFlow(trend); + if (has) + { + // + bool isBullish = IsBullish(trend); + + // + count = conditions.CountOrderFlow(); + for (int i = 0; i < count; i++) + { + // + XOBFVG iOBFVG = conditions.orderFlow[i]; + + // + XCBaseObject *drawnObjects[]; + int drawnObjectsCount = drawer + .DrawOBFVG( + // + iOBFVG, + drawnObjects, + // + true, // Draw Order Block ... + true, // Draw Fair Value Gap ... + false, // Draw Supply Zones ... + false, // Draw Demand Zones ... + false, // Draw Support Zones ... + false // Draw Resistance Zones ... + ); + if (IsValidSize(drawnObjectsCount)) + { + AddDrawnObjects(drawnObjects); + } + } + + // + if (drawTrendLine) + { + // + XOBFVGPivot pivots[]; + count = conditions.GetSwings(pivots); + has = count >= 2; + if (has) + { + // + // Get Max / Min ... + double max = 0; + double min = 0; + datetime maxAt = NULL; + datetime minAt = NULL; + for (int i = 0; i < count; i++) + { + // + XOBFVGPivot iPivot = pivots[i]; + + // + if (!iPivot.IsValid()) + { + // + iPivot.Clean(); + continue; + } + + // + // Min ... + bool canSetMin = + min == 0 + ? true + : min > iPivot.value; + if (canSetMin) + { + // + minAt = iPivot.at; + min = iPivot.value; + } + + // + // Max ... + bool canSetMax = + max == 0 + ? true + : max < iPivot.value; + if (canSetMax) + { + // + maxAt = iPivot.at; + max = iPivot.value; + } + + // + iPivot.Clean(); + } + + // + double price1 = + isBullish + ? min + : max; + double price2 = + isBullish + ? max + : min; + datetime time1 = + isBullish + ? minAt + : maxAt; + datetime time2 = + isBullish + ? maxAt + : minAt; + + // + color clrTrend = + isBullish + ? bullishColor + : bearishColor; + + // + string trendName = + ToString(trend) + "_" + "XTRNDL_" + + ToString(conditions.period) + "_" + + ToString(price1) + "_" + + ToString(price2) + "_" + + ToMD5(ToString(time1)); + + // + CChartObjectTrend *mTrend; + mTrend = new CChartObjectTrend(); + isCreated = mTrend.Create( + chartID, + trendName, + subWindow, + time1, + price1, + time2, + price2 // + ); + + // + if (isCreated) + { + // + mTrend.Width(2); + mTrend.Color(clrTrend); + mTrend.RayRight(true); + + // + mObjects.Add(mTrend); + } + } + } + } } // @@ -1663,6 +1841,24 @@ class XCX121SMCXWZSignalDetector : public XCBase return result; } + // + void AddDrawnObjects(XCBaseObject *&objects[]) + { + // + int count = ArraySize(objects); + bool has = IsValidSize(count); + if (!has) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + mObjects.Add(objects[i]); + } + } + // }; diff --git a/X121SMCEA/Signals/x-121.smc.xwz.signal.lib.mq5 b/X121SMCEA/Signals/x-121.smc.xwz.signal.lib.mq5 index f00b8ce4..f8a94fe3 100644 --- a/X121SMCEA/Signals/x-121.smc.xwz.signal.lib.mq5 +++ b/X121SMCEA/Signals/x-121.smc.xwz.signal.lib.mq5 @@ -37,306 +37,6 @@ enum ENUM_X121SMC_XWZ_PRIORITY X121SMC_XWZ_PRIORITY_STRONG_HIGH, }; -// -struct X121SMCXWZZone -{ - // - // Props ... - XCFVG *fvg; - XCOrderBlock *ob; - XCSupplyZone *supply; - XCDemandZone *demand; - XCSupportZone *support; - XCResistanceZone *resistance; - - // - // Constructor ... - X121SMCXWZZone() - { - Clean(); - } - - // - // Tools ... - - // - void Clean() - { - // - ZeroMemory(ob); - ZeroMemory(fvg); - ZeroMemory(supply); - ZeroMemory(demand); - ZeroMemory(support); - ZeroMemory(resistance); - } - - // - bool IsValid() - { - // - return HasOB() && - HasFVG() && - HasDirection(); - } - - // - bool AsBox(XBoxZone &box) - { - // - bool result = false; - - // - box.Clean(); - - // - result = IsValid() && - HasDirection(); - if (!result) - { - return result; - } - - // - XBoxZone obBox; - bool hasOBBox = ToBox( - obBox, - ob // - ); - result = hasOBBox; - if (!result) - { - // - box.Clean(); - obBox.Clean(); - return result; - } - - // - box = obBox; - - // - obBox.Clean(); - - // - bool hasOB = HasOB(); - bool hasFVG = HasFVG(); - bool hasSupply = HasSupply(); - bool hasDemand = HasDemand(); - bool hasSupport = HasSupport(); - bool hasResistance = HasResistance(); - - // - // OB Compared since box used obBox ... - - // - if (hasFVG) - { - // - if (fvg.Upper() > box.upper) - { - box.upper = fvg.Upper(); - } - - // - if (fvg.Lower() < box.lower) - { - box.lower = fvg.Lower(); - } - } - - // - result = box.IsValid(); - - // - return result; - } - - // - bool HasOB() - { - return ob != NULL && - ob.IsValid(); - } - - // - bool HasFVG() - { - return fvg != NULL && - fvg.IsValid(); - } - - // - bool HasSupply() - { - return supply != NULL && - supply.IsValid(); - } - - // - bool HasDemand() - { - return demand != NULL && - demand.IsValid(); - } - - // - bool HasSupport() - { - return support != NULL && - support.IsValid(); - } - - // - bool HasResistance() - { - return resistance != NULL && - resistance.IsValid(); - } - - // - bool IsBullish() - { - // - return HasOB() && - ob.IsBullish() && - HasFVG() && - fvg.IsBullish(); - } - - // - bool IsBearish() - { - // - return HasOB() && - ob.IsBearish() && - HasFVG() && - fvg.IsBearish(); - } - - // - bool HasDirection() - { - return IsBullish() || - IsBearish(); - } - - // - bool ValidateZone( - int barIndex = 0, - ENUM_TIMEFRAMES forPeriod = NULL, - bool forceOB = false, - bool forceFVG = false // - ) - { - // - bool result = false; - - // - if (barIndex < 0) - { - barIndex = 0; - } - - // - result = IsValid() && - HasDirection(); - if (!result) - { - return result; - } - - // - string symbol = ob.GetSymbol(); - if (forPeriod == NULL) - { - forPeriod = ob.GetPeriod(); - } - ENUM_X_DIRECTION forDir = GetDirection(); - - // - XOHCL cBar; - result = cBar.Init( - symbol, - forPeriod, - barIndex + 1 // - ); - if (!result) - { - // - cBar.Clean(); - return result; - } - - // - bool isObBreaked = - IsBullish(forDir) - ? cBar.close < ob.Lower() - : cBar.close > ob.Upper(); - - // - bool isFVGBreaked = - IsBullish(forDir) - ? cBar.close < fvg.Lower() - : cBar.close > fvg.Upper(); - - // - bool isBreaked = true; - if ((forceOB && forceFVG) || - (!forceOB && !forceFVG)) - { - isBreaked = - isObBreaked || - isFVGBreaked; - } - else if (forceFVG) - { - isBreaked = isFVGBreaked; - } - else if (forceOB) - { - isBreaked = isObBreaked; - } - - // - result = !isBreaked; - - // - cBar.Clean(); - - // - return result; - } - - // - ENUM_X_DIRECTION GetDirection() - { - // - ENUM_X_DIRECTION result = X_DIRECTION_NONE; - - // - bool hasDirection = HasDirection(); - if (!hasDirection) - { - return result; - } - - // - bool isBullish = IsBullish(); - - // - result = - isBullish - ? X_DIRECTION_BULLISH - : X_DIRECTION_BEARISH; - - // - return result; - } - - // -}; - // // Definitions ... struct X121SMCStrategyXWZSignalConditions @@ -371,9 +71,7 @@ struct X121SMCStrategyXWZSignalConditions ENUM_X121SMC_XWZ_PRIORITY priority; // - // X121SMCXWZZone startZone; - X121SMCXWZZone bullishFlow[]; - X121SMCXWZZone bearishFlow[]; + XOBFVG orderFlow[]; // // Constructor ... @@ -503,8 +201,7 @@ struct X121SMCStrategyXWZSignalConditions // // startZone.Clean(); - Clean(bullishFlow); - Clean(bearishFlow); + Clean(orderFlow); } // @@ -512,27 +209,93 @@ struct X121SMCStrategyXWZSignalConditions // // - bool HasBullishFlow() + bool HasOrderFlow() { - return HasChild(bullishFlow); + // + bool result = false; + + // + result = HasChild(orderFlow); + + // + return result; } // - int CountBullishFlow() + bool HasOrderFlow( + ENUM_X_DIRECTION &orderFlowDir // + ) { - return ArraySize(bullishFlow); + // + bool result = false; + + // + result = HasChild(orderFlow); + if (result) + { + orderFlowDir = orderFlow[0].GetDirection(); + } + + // + return result; } // - bool HasBearishFlow() + int CountOrderFlow() { - return HasChild(bearishFlow); + return ArraySize(orderFlow); } // - int CountBearishFlow() + int GetSwings( + XOBFVGPivot &pivots[] // + ) { - return ArraySize(bearishFlow); + // + int result = 0; + + // + Clean(pivots); + + // + int count = 0; + bool has = false; + + // + has = HasOrderFlow(); + if (!has) + { + return result; + } + + // + count = CountOrderFlow(); + for (int i = 0; i < count; i++) + { + // + XOBFVGPivot iPivots[]; + int iPivotsCount = orderFlow[i] + .GetSwings(iPivots); + bool hasPivots = IsValidSize(iPivotsCount); + if (hasPivots) + { + // + Copy( + iPivots, + pivots, + false // + ); + } + + // + Clean(iPivots); + } + + // + result = ArraySize(pivots); + + // + return result; } // @@ -542,6 +305,22 @@ struct X121SMCStrategyXWZSignalConditions value = NormalizeTime(value); // + int count = 0; + bool has = false; + + // + // Order Flow ... + ENUM_X_DIRECTION orderFlowDir; + has = HasOrderFlow(orderFlowDir); + if (has) + { + // + count = CountOrderFlow(); + for (int i = 0; i < count; i++) + { + orderFlow[i].UpdateTo(value); + } + } } // @@ -550,18 +329,19 @@ struct X121SMCStrategyXWZSignalConditions // int DetectOrderFlow( - ENUM_X_DIRECTION forDir, + ENUM_X_DIRECTION &orderFlowDir, XPOIState &htfState, XPOIState &mtfState, - XPOIState &stfState, - int barIndex = 0, - ENUM_TIMEFRAMES forPeriod = NULL, - XCPOIDrawer *drawer = NULL // + XPOIState &stfState // ) { // int result = 0; + // + Clean(orderFlow); + orderFlowDir = X_DIRECTION_NONE; + // bool isValid = // @@ -569,8 +349,7 @@ struct X121SMCStrategyXWZSignalConditions IsValid(period) && htfState.HasChild() && mtfState.HasChild() && - stfState.HasChild() && - HasDirection(forDir) + stfState.HasChild() // ; if (!isValid) @@ -579,27 +358,14 @@ struct X121SMCStrategyXWZSignalConditions } // - if (forPeriod == NULL) - { - forPeriod = period; - } - - // - bool isBullish = IsBullish(forDir); - - // - int idx = -1; - int count = 0; - bool has = false; - - // - XOBFVG tmpOBFVGs[]; - count = ExtractOBFVGZones( + // Bullish OBFVGs ... + XOBFVG bullOBFVGs[]; + int bullOBFVGsCount = ExtractOBFVGZones( htfState, mtfState, stfState, - tmpOBFVGs, - forDir, + bullOBFVGs, + X_DIRECTION_BULLISH, NULL, // For Period ... // // Start ... @@ -624,169 +390,107 @@ struct X121SMCStrategyXWZSignalConditions // true, // Ignore Inside in Place Filter ... // - true, // Detect Inside OB POI(s) ... - false, // Detect Inside FVG POI(s) ... + true, // Detect Inside OB POI(s) ... + true, // Detect Inside FVG POI(s) ... true // Apply From Filter to Selected POI(s) ... ); - has = IsValidSize(count); - if (has) + bool hasBullOBFVGs = IsValidSize(bullOBFVGsCount); + + // + // Bearish OBFVGs ... + XOBFVG bearOBFVGs[]; + int bearOBFVGsCount = ExtractOBFVGZones( + htfState, + mtfState, + stfState, + bearOBFVGs, + X_DIRECTION_BEARISH, + NULL, // For Period ... + // + // Start ... + XZONE_STATE_PASSED, + true, // Force Body Out ... + // + // Activation ... + XZONE_STATE_PASSED, + true, // Force Body In ... + // + // Breaktion ... + XZONE_STATE_FAILED, + false, // Force Body In ... + // + // Works ... + XZONE_STATE_PASSED, + true, // Force Body Out ... + // + true, // Apply Sort ... + true, // From Filter ... + true, // Place Filter ... + // + true, // Ignore Inside in Place Filter ... + // + true, // Detect Inside OB POI(s) ... + true, // Detect Inside FVG POI(s) ... + true // Apply From Filter to Selected POI(s) ... + ); + bool hasBearOBFVGs = IsValidSize(bearOBFVGsCount); + + // + bool hasOrderBlow = hasBearOBFVGs || + hasBullOBFVGs; + if (hasOrderBlow) { // - for (int i = 0; i < count; i++) + // Vlidate ... + bool isValid = + // + (hasBullOBFVGs && + !hasBearOBFVGs && + bullOBFVGsCount > 1) + // + || + // + (hasBearOBFVGs && + !hasBullOBFVGs && + bearOBFVGsCount > 1) + // + ; + + // + if (isValid) { // - XOBFVG iOBFVG = tmpOBFVGs[i]; - iOBFVG.UpdateTo(NULL); - - // // - // if (iOBFVG.HasOB()) - // { - // // - // XCBaseObject *iObj; - // drawer.CreateOrderBlock( - // iOBFVG.ob, - // iObj // - // ); - - // // - // if (stfState.HasSupplyZones()) - // { - // // - // for (int j = 0; j < stfState.CountSupplyZones(); j++) - // { - // // - // XCSupplyZone *jZone = stfState.supplyZones[j]; - - // // - // bool isInside = IsInsideDiff( - // jZone, - // iOBFVG.ob, - // false // - // ); - - // // - // if (isInside) - // { - // // - // XCSupplyZoneObject *iObj; - // drawer.CreateSupplyZone( - // jZone, - // iObj // - // ); - // } - // } - // } - - // // - // if (stfState.HasDemandZones()) - // { - // // - // for (int j = 0; j < stfState.CountDemandZones(); j++) - // { - // // - // XCDemandZone *jZone = stfState.demandZones[j]; - - // // - // bool isInside = IsInsideDiff( - // jZone, - // iOBFVG.ob, - // false // - // ); - - // // - // if (isInside) - // { - // // - // XCDemandZoneObject *iObj; - // drawer.CreateDemandZone( - // jZone, - // iObj // - // ); - // } - // } - // } - - // // - // if (stfState.HasSupportZones()) - // { - // // - // for (int j = 0; j < stfState.CountSupportZones(); j++) - // { - // // - // XCSupportZone *jZone = stfState.supportZones[j]; - - // // - // bool isInside = IsInsideDiff( - // jZone, - // iOBFVG.ob, - // false // - // ); - - // // - // if (isInside) - // { - // // - // XCSupportZoneObject *iObj; - // drawer.CreateSupportZone( - // jZone, - // iObj // - // ); - // } - // } - // } - - // // - // if (stfState.HasResistanceZones()) - // { - // // - // for (int j = 0; j < stfState.CountResistanceZones(); j++) - // { - // // - // XCResistanceZone *jZone = stfState.resistanceZones[j]; - - // // - // bool isInside = IsInsideDiff( - // jZone, - // iOBFVG.ob, - // false // - // ); - - // // - // if (isInside) - // { - // // - // XCResistanceZoneObject *iObj; - // drawer.CreateResistanceZone( - // jZone, - // iObj // - // ); - // } - // } - // } - // } - - // - XCBaseObject *drawnObjects[]; - int drawnObjectsCount = drawer.DrawOBFVG( - iOBFVG, - drawnObjects, + if (hasBullOBFVGs) + { // - true, // Draw Order Block ... - true, // Draw Fair Value Gap ... - false, // Draw Supply Zones ... - false, // Draw Demand Zones ... - false, // Draw Support Zones ... - false // Draw Resistance Zones ... - ); + Copy( + bullOBFVGs, + orderFlow // + ); + } + else if (hasBearOBFVGs) + { + // + Copy( + bearOBFVGs, + orderFlow // + ); + } // - iOBFVG.Clean(); + orderFlowDir = + hasBullOBFVGs + ? X_DIRECTION_BULLISH + : X_DIRECTION_BEARISH; + + // + result = ArraySize(orderFlow); } } // - has = IsValidSize(count); - result = count; + Clean(bullOBFVGs); + Clean(bearOBFVGs); // return result;