/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Seriallize Library // --------------------------------------- // Name: XOBFVGLib // Description: All models related to POIs ... // // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property library #property copyright "Copyright `023, SaherElm IT Center" #property link "https://www.saherelm.ir" #property version "1.00" #property strict // // Imports ... #include "../Libraries/x-saherelm.x-poi.lib.mq5" #include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5" // 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 { // // Props ... XCFVG *fvg; XCOrderBlock *ob; // XCSupplyZone *supplyZones[]; XCDemandZone *demandZones[]; // XCSupportZone *supportZones[]; XCResistanceZone *resistanceZones[]; // // Constructor ... XOBFVG() { Clean(); } // // Tools ... // void Clean() { // ZeroMemory(ob); ZeroMemory(fvg); // Clean(supplyZones); Clean(demandZones); Clean(supportZones); Clean(resistanceZones); // ZeroMemory(this); } // bool IsValid() { return HasOB() && HasFVG(); } // bool HasOB() { return ob != NULL && ob.IsValid(); } // bool HasFVG() { return fvg != NULL && fvg.IsValid(); } // bool HasSupplyZones() { return HasChild(supplyZones); } // int CountSupplyZones() { return ArraySize(supplyZones); } // bool HasDemandZones() { return HasChild(demandZones); } // int CountDemandZones() { return ArraySize(demandZones); } // bool HasSupportZones() { return HasChild(supportZones); } // int CountSupportZones() { return ArraySize(supportZones); } // bool HasResistanceZones() { return HasChild(resistanceZones); } // int CountResistanceZones() { 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 UpdateZones( XPOIState &state // ) { // Clean(supplyZones); Clean(demandZones); Clean(supportZones); Clean(resistanceZones); // bool isValid = IsValid() && state.HasChild(); if (!isValid) { return; } // int count = 0; bool has = false; bool isPassed = false; // // Supply Zones ... has = state.HasSupplyZones(); if (has) { // count = state.CountSupplyZones(); for (int i = 0; i < count; i++) { // XCSupplyZone *iZone = state.supplyZones[i]; // isPassed = iZone.From() > ob.From(); if (!isPassed) { // ZeroMemory(iZone); continue; } // isPassed = IsInsideDiff( iZone, ob // ); if (!isPassed) { // ZeroMemory(iZone); continue; } // Add( iZone, supplyZones // ); } } // // Demand Zones ... has = state.HasDemandZones(); if (has) { // count = state.CountDemandZones(); for (int i = 0; i < count; i++) { // XCDemandZone *iZone = state.demandZones[i]; // isPassed = iZone.From() > ob.From(); if (!isPassed) { // ZeroMemory(iZone); continue; } // isPassed = IsInsideDiff( iZone, ob // ); if (!isPassed) { // ZeroMemory(iZone); continue; } // Add( iZone, demandZones // ); } } // // Support Zones ... has = state.HasSupportZones(); if (has) { // count = state.CountSupportZones(); for (int i = 0; i < count; i++) { // XCSupportZone *iZone = state.supportZones[i]; // isPassed = iZone.From() > ob.From(); if (!isPassed) { // ZeroMemory(iZone); continue; } // isPassed = IsInsideDiff( iZone, ob // ); if (!isPassed) { // ZeroMemory(iZone); continue; } // Add( iZone, supportZones // ); } } // // Resistance Zones ... has = state.HasResistanceZones(); if (has) { // count = state.CountResistanceZones(); for (int i = 0; i < count; i++) { // XCResistanceZone *iZone = state.resistanceZones[i]; // isPassed = iZone.From() > ob.From(); if (!isPassed) { // ZeroMemory(iZone); continue; } // isPassed = IsInsideDiff( iZone, ob // ); if (!isPassed) { // ZeroMemory(iZone); continue; } // Add( iZone, resistanceZones // ); } } } // void UpdateTo(datetime value = NULL) { // value = NormalizeTime(value); // int count = 0; bool has = false; // has = HasOB(); if (has) { ob.To(value); } // has = HasFVG(); if (has) { fvg.To(value); } // // Supply Zones ... has = HasSupplyZones(); if (has) { // count = CountSupplyZones(); for (int i = 0; i < count; i++) { supplyZones[i].To(value); } } // // Demand Zones ... has = HasDemandZones(); if (has) { // count = CountDemandZones(); for (int i = 0; i < count; i++) { demandZones[i].To(value); } } // // Support Zones ... has = HasSupportZones(); if (has) { // count = CountSupportZones(); for (int i = 0; i < count; i++) { supportZones[i].To(value); } } // // Resistance Zones ... has = HasResistanceZones(); if (has) { // count = CountResistanceZones(); for (int i = 0; i < count; i++) { resistanceZones[i].To(value); } } } }; // // // /** * Extract XOBFVG Collection of Market ... * * @param htfState: Higher Time Frame POI State (Used For Detecting Order Blocks) ... * @param mtfState: Medium Time Frame POI State (Used For Detecting Fair Value Gaps) ... * @param stfState: Short Time Frame POI State (Used For Detecting Other POI(s)) ... * @param dest: Result Collection ... * @param forDir: Market Direction for Detecting POI(s) ... * @param forPeriod: Time Frame which Used for Zones actions (NULL for Default) ... * @param startState: Zones Start State ... * @param forceStartedBodyOut: Zones Started Bar's Body State ... * @param activeState: Zones Activation State ... * @param forceActivatedBodyIn: Zones Activated Bar's Body State ... * @param breakState: Zones Breaked State ... * @param forceBreakedBodyOut: Zone Breaker Bar's Body State ... * @param workState: Zones Worked State ... * @param forceWorkedBodyOut: Zones Worked Bar's Body State ... * @param applySort: Specify Applying Sorting on Collections ... * @param applyFromFilter: Filter Sorted Zones based on Occured Date ... * @param applyPlaceFilter: Filter Sorted Zones based on Occured Place ... * @param ignoreInside: Ignore Inside Sides of POI(s) Zones in Related to Order Block ... * @param applyFromFilterForPOIs: Apply From Filter for Other POI(s) ... * * @return ( int ) */ int ExtractOBFVGZones( XPOIState &htfState, XPOIState &mtfState, XPOIState &stfState, XOBFVG &dest[], // ENUM_X_DIRECTION forDir, ENUM_TIMEFRAMES forPeriod = NULL, // // Start ... ENUM_XZONE_STATE startState = XZONE_STATE_PASSED, bool forceStartedBodyOut = false, // // Activation ... ENUM_XZONE_STATE activeState = XZONE_STATE_FAILED, bool forceActivatedBodyIn = false, // // Breaktion ... ENUM_XZONE_STATE breakState = XZONE_STATE_FAILED, bool forceBreakedBodyOut = false, // // Works ... ENUM_XZONE_STATE workState = XZONE_STATE_FAILED, bool forceWorkedBodyOut = false, // bool applySort = true, bool applyFromFilter = true, bool applyPlaceFilter = true, // bool ignoreInside = true, // bool detectInsideOBPOIs = true, bool detectInsideFVGPOIs = true, bool applyFromFilterForPOIs = true // ) { // int result = 0; // Clean(dest); // bool isValid = // htfState.HasChild() && mtfState.HasChild() && stfState.HasChild() && HasDirection(forDir) // ; if (!isValid) { return result; } // // Implement Logic ... // int idx = -1; int count = 0; bool has = false; XCFVG *tmpFVGs[]; XOBFVG tmpOBFVGs[]; XCOrderBlock *tmpOBs[]; XCOrderBlock *tmpWorkingOBs[]; datetime cTime = TimeCurrent(); bool isBullish = IsBullish(forDir); // if (isBullish) { // has = htfState.HasBullishOrderBlocks(); if (has) { // Copy( htfState.bullishOrderBlocks, tmpOBs // ); // has = mtfState.HasBullishFairValueGaps(); if (has) { // Copy( mtfState.bullishFairValueGaps, tmpFVGs // ); } } } else { // has = htfState.HasBearishOrderBlocks(); if (has) { // Copy( htfState.bearishOrderBlocks, tmpOBs // ); // has = mtfState.HasBearishFairValueGaps(); if (has) { // Copy( mtfState.bearishFairValueGaps, tmpFVGs // ); } } } // count = ArraySize(tmpOBs); has = IsValidSize(count); if (has) { // for (int i = 0; i < count; i++) { // XCOrderBlock *iOB = tmpOBs[i]; // // Check Started State ... if (startState == XZONE_STATE_PASSED || startState == XZONE_STATE_FAILED) { // XOHCL passedBar; bool isBodyOut = false; bool isStarted = iOB.IsStarted( forDir, passedBar, isBodyOut, forPeriod // ); // bool isPassed = startState == XZONE_STATE_FAILED ? !isStarted : !forceStartedBodyOut ? isStarted : isStarted && isBodyOut; // passedBar.Clean(); if (!isPassed) { continue; } } // // Check Activation State ... if (activeState == XZONE_STATE_PASSED || activeState == XZONE_STATE_FAILED) { // XOHCL passedBar; bool isBodyIn = false; bool isActivated = iOB.IsActivated( forDir, passedBar, isBodyIn, forceStartedBodyOut, forPeriod // ); // bool isPassed = activeState == XZONE_STATE_FAILED ? !isActivated : !forceActivatedBodyIn ? isActivated : isActivated && isBodyIn; // passedBar.Clean(); if (!isPassed) { continue; } } // // Check Break State ... if (breakState == XZONE_STATE_PASSED || breakState == XZONE_STATE_FAILED) { // XOHCL passedBar; bool isBodyOut = false; bool isBreaked = iOB.IsBreaked( forDir, passedBar, isBodyOut, forceStartedBodyOut, forPeriod // ); // bool isPassed = breakState == XZONE_STATE_FAILED ? !isBreaked : !forceBreakedBodyOut ? isBreaked : isBreaked && isBodyOut; // passedBar.Clean(); if (!isPassed) { continue; } } // // Check Work State ... if (workState == XZONE_STATE_PASSED || workState == XZONE_STATE_FAILED) { // bool ignoreActivation = activeState != XZONE_STATE_PASSED; // XOHCL passedBar; bool isBodyOut = false; bool isWorked = iOB.IsWorked( forDir, passedBar, isBodyOut, ignoreActivation, forceStartedBodyOut, forceActivatedBodyIn, forPeriod // ); // bool isPassed = workState == XZONE_STATE_FAILED ? !isWorked : !forceWorkedBodyOut ? isWorked : isWorked && isBodyOut; // passedBar.Clean(); if (!isPassed) { continue; } } // has = FindIndex( idx, iOB, tmpWorkingOBs // ); if (has) { // ZeroMemory(iOB); continue; } // Add( iOB, tmpWorkingOBs // ); } } // // Clean ... Clean(tmpOBs); has = HasChild(tmpWorkingOBs); if (has) { // Clean(tmpOBs); Copy( tmpWorkingOBs, tmpOBs // ); // Clean(tmpWorkingOBs); } // // Sorting ... if (applySort) { // Clean(tmpWorkingOBs); has = HasChild(tmpOBs); count = ArraySize(tmpOBs); // // Sorting Applied Only when there is more than one item ... if (has && count > 1) { // bool hasLast = false; XCOrderBlock *last = NULL; while (HasChild(tmpOBs)) { // // Extract First Last Item ... if (!hasLast) { // idx = isBullish ? GetLowest(tmpOBs) : GetHighest(tmpOBs); hasLast = IsValidIndex(idx); if (hasLast) { // last = tmpOBs[idx]; ArrayRemove( tmpOBs, idx, 1 // ); // Add( last, tmpWorkingOBs // ); } } // has = false; XCOrderBlock *iOB = NULL; idx = isBullish ? GetLowest(tmpOBs) : GetHighest(tmpOBs); has = IsValidIndex(idx); if (has) { // iOB = tmpOBs[idx]; ArrayRemove( tmpOBs, idx, 1 // ); } // if (!has) { break; } // // Apply From Filter ... if (applyFromFilter) { // bool isFromPassed = hasLast && last.From() < iOB.From(); if (!isFromPassed) { continue; } } // // Apply Place Filter ... if (applyPlaceFilter) { // bool isAbove = hasLast && IsAbove( iOB, last, ignoreInside // ); // bool isBelow = hasLast && IsBelow( iOB, last, ignoreInside // ); // bool isPlacePassed = isBullish ? isAbove : isBelow; if (!isPlacePassed) { continue; } } // last = iOB; Add( iOB, tmpWorkingOBs // ); } // if (hasLast && !HasChild(tmpWorkingOBs)) { // Add( last, tmpWorkingOBs // ); } } else { // Copy( tmpOBs, tmpWorkingOBs // ); } // if (HasChild(tmpWorkingOBs)) { // Copy( tmpWorkingOBs, tmpOBs // ); } // } // count = ArraySize(tmpOBs); has = IsValidSize(count); // // Converts to XOBFVG ... if (has) { // for (int i = 0; i < count; i++) { // XOBFVG iOBFVG; // iOBFVG.ob = tmpOBs[i]; // AddRef( iOBFVG, tmpOBFVGs // ); // iOBFVG.Clean(); } } // Clean(dest); Clean(tmpOBs); Clean(tmpWorkingOBs); // // Here we Going to Complete XOBFVGs ... has = HasChild(tmpOBFVGs); if (has && HasChild(tmpFVGs)) { // count = ArraySize(tmpOBFVGs); for (int i = 0; i < count; i++) { // XOBFVG iOBFVG = tmpOBFVGs[i]; // // Detecting FVG ... XCFVG *tmpWorkingFVGs[]; for (int j = 0; j < ArraySize(tmpFVGs); j++) { // XCFVG *jFVG = tmpFVGs[j]; // // Check Start Date ... bool isFromPassed = jFVG.From() > iOBFVG.ob.From(); if (!isFromPassed) { // ZeroMemory(jFVG); continue; } // // Check Place ... // bool isAbove = IsAboveDiff( jFVG, iOBFVG.ob, ignoreInside // ); // bool isBelow = IsBelowDiff( jFVG, iOBFVG.ob, ignoreInside // ); // bool isInside = IsInsideDiff( jFVG, iOBFVG.ob, false // ); // bool isPlacePassed = isInside || (isBullish ? isAbove : isBelow); if (!isPlacePassed) { // ZeroMemory(jFVG); continue; } // Add( jFVG, tmpWorkingFVGs // ); } // int fvgsCount = ArraySize(tmpWorkingFVGs); has = IsValidSize(fvgsCount); if (has) { // idx = isBullish ? GetLowest(tmpWorkingFVGs) : GetHighest(tmpWorkingFVGs); // if (IsValidIndex(idx)) { // XCFVG *selectedFVG = tmpWorkingFVGs[idx]; // iOBFVG.fvg = selectedFVG; // bool isExists = FindIndex( idx, selectedFVG, tmpFVGs // ); if (isExists) { // ArrayRemove( tmpFVGs, idx, 1 // ); } } } Clean(tmpWorkingFVGs); // bool isFVGPassed = iOBFVG.HasFVG(); if (!isFVGPassed) { // iOBFVG.Clean(); continue; } // // Validate Model ... bool isValid = iOBFVG.IsValid(); if (!isValid) { // iOBFVG.Clean(); continue; } // iOBFVG.UpdateTo(cTime); // AddRef( iOBFVG, dest // ); // iOBFVG.Clean(); } // Clean(tmpOBFVGs); } // // Detecting Other POI(s) ... has = HasChild(dest); bool canDetectPOIs = detectInsideOBPOIs || detectInsideFVGPOIs; if (has && canDetectPOIs) { // count = ArraySize(dest); for (int i = 0; i < count; i++) { // // Supply Zones ... has = stfState.HasSupplyZones(); if (has) { // for (int j = 0; j < stfState.CountSupplyZones(); j++) { // bool isInsideOB = IsInsideDiff( stfState.supplyZones[j], dest[i].ob // ); // bool isInsideFVG = IsInsideDiff( stfState.supplyZones[j], dest[i].fvg // ); // bool isInside = isInsideOB || isInsideFVG; if (isInside) { // bool isPassed = detectInsideOBPOIs ? isInsideOB : detectInsideFVGPOIs ? isInsideFVG : false; if (isPassed) { // isPassed = !applyFromFilterForPOIs ? isPassed : isInsideOB ? stfState.supplyZones[j].From() > dest[i].ob.From() : isInsideFVG ? stfState.supplyZones[j].From() > dest[i].fvg.From() : false; } // if (isPassed) { // Add( stfState.supplyZones[j], dest[i].supplyZones // ); } } } } // // Demand Zones ... has = stfState.HasDemandZones(); if (has) { // for (int j = 0; j < stfState.CountDemandZones(); j++) { // bool isInsideOB = IsInsideDiff( stfState.demandZones[j], dest[i].ob // ); // bool isInsideFVG = IsInsideDiff( stfState.demandZones[j], dest[i].fvg // ); // bool isInside = isInsideOB || isInsideFVG; if (isInside) { // bool isPassed = detectInsideOBPOIs ? isInsideOB : detectInsideFVGPOIs ? isInsideFVG : false; if (isPassed) { // isPassed = !applyFromFilterForPOIs ? isPassed : isInsideOB ? stfState.demandZones[j].From() > dest[i].ob.From() : isInsideFVG ? stfState.demandZones[j].From() > dest[i].fvg.From() : false; } // if (isPassed) { // Add( stfState.demandZones[j], dest[i].demandZones // ); } } } } // // Support Zones ... has = stfState.HasSupportZones(); if (has) { // for (int j = 0; j < stfState.CountSupportZones(); j++) { // bool isInsideOB = IsInsideDiff( stfState.supportZones[j], dest[i].ob // ); // bool isInsideFVG = IsInsideDiff( stfState.supportZones[j], dest[i].fvg // ); // bool isInside = isInsideOB || isInsideFVG; if (isInside) { // bool isPassed = detectInsideOBPOIs ? isInsideOB : detectInsideFVGPOIs ? isInsideFVG : false; if (isPassed) { // isPassed = !applyFromFilterForPOIs ? isPassed : isInsideOB ? stfState.supportZones[j].From() > dest[i].ob.From() : isInsideFVG ? stfState.supportZones[j].From() > dest[i].fvg.From() : false; } // if (isPassed) { // Add( stfState.supportZones[j], dest[i].supportZones // ); } } } } // // Resistance Zones ... has = stfState.HasResistanceZones(); if (has) { // for (int j = 0; j < stfState.CountResistanceZones(); j++) { // bool isInsideOB = IsInsideDiff( stfState.resistanceZones[j], dest[i].ob // ); // bool isInsideFVG = IsInsideDiff( stfState.resistanceZones[j], dest[i].fvg // ); // bool isInside = isInsideOB || isInsideFVG; if (isInside) { // bool isPassed = detectInsideOBPOIs ? isInsideOB : detectInsideFVGPOIs ? isInsideFVG : false; if (isPassed) { // isPassed = !applyFromFilterForPOIs ? isPassed : isInsideOB ? stfState.resistanceZones[j].From() > dest[i].ob.From() : isInsideFVG ? stfState.resistanceZones[j].From() > dest[i].fvg.From() : false; } // if (isPassed) { // Add( stfState.resistanceZones[j], dest[i].resistanceZones // ); } } } } } } // Clean(tmpOBs); Clean(tmpFVGs); Clean(tmpOBFVGs); // result = ArraySize(dest); // return result; } /** * Find Highest Order Block of XOBFVG Collection ... * * @param source: XOBFVG Collection ... * * @return ( int ) */ int GetHighest( XOBFVG &source[] // ) { // int result = -1; // int count = ArraySize(source); bool has = IsValidSize(count); if (!has) { return result; } // XOBFVG item; for (int i = 0; i < count; i++) { // XOBFVG iOBFVG = source[i]; // bool canSet = !item.IsValid() ? true : item.ob.Upper() < iOBFVG.ob.Upper(); if (canSet) { // item = iOBFVG; result = i; } // iOBFVG.Clean(); } // item.Clean(); // return result; } /** * Find Lowest Order Block of XOBFVG Collection ... * * @param source: XOBFVG Collection ... * * @return ( int ) */ int GetLowest( XOBFVG &source[] // ) { // int result = -1; // int count = ArraySize(source); bool has = IsValidSize(count); if (!has) { return result; } // XOBFVG item; for (int i = 0; i < count; i++) { // XOBFVG iOBFVG = source[i]; // bool canSet = !item.IsValid() ? true : item.ob.Lower() > iOBFVG.ob.Lower(); if (canSet) { // item = iOBFVG; result = i; } // iOBFVG.Clean(); } // item.Clean(); // return result; } /** * Find Youngest Order Block of XOBFVG Collection ... * * @param source: XOBFVG Collection ... * * @return ( int ) */ int GetYoungest( XOBFVG &source[] // ) { // int result = -1; // int count = ArraySize(source); bool has = IsValidSize(count); if (!has) { return result; } // XOBFVG item; for (int i = 0; i < count; i++) { // XOBFVG iOBFVG = source[i]; // bool canSet = !item.IsValid() ? true : item.ob.From() < iOBFVG.ob.From(); if (canSet) { // item = iOBFVG; result = i; } // iOBFVG.Clean(); } // item.Clean(); // return result; } /** * Find Oldest Order Block of XOBFVG Collection ... * * @param source: XOBFVG Collection ... * * @return ( int ) */ int GetOldest( XOBFVG &source[] // ) { // int result = -1; // int count = ArraySize(source); bool has = IsValidSize(count); if (!has) { return result; } // XOBFVG item; for (int i = 0; i < count; i++) { // XOBFVG iOBFVG = source[i]; // bool canSet = !item.IsValid() ? true : item.ob.From() > iOBFVG.ob.From(); if (canSet) { // item = iOBFVG; result = i; } // iOBFVG.Clean(); } // item.Clean(); // return result; } /** * Sort XOBFVG Collection based on OB Place ... * * @param source: XOBFVG instance Collection ... * @param forDir: ENUM_X_DIRECTION member ... * @param ignoreInside: Specified Ignoring price inside in Sorting ... */ void Sort( XOBFVG &source[], ENUM_X_DIRECTION forDir, bool ignoreInside = true // ) { // int idx = -1; int count = 0; bool has = false; // has = HasDirection(forDir); if (!has) { return; } // bool isBullish = IsBullish(forDir); // count = ArraySize(source); has = IsValidSize(count); if (!has) { return; } // XOBFVG last; XOBFVG tmpItems[]; bool hasLast = false; while (HasChild(source)) { // if (!hasLast) { // idx = GetOldest(source); hasLast = IsValidIndex(idx); if (hasLast) { // last = source[idx]; ArrayRemove( source, idx, 1 // ); // AddRef( last, tmpItems // ); } // count = ArraySize(source); has = IsValidSize(count); if (!has) { // last.Clean(); break; } } if (!hasLast) { // last.Clean(); break; } // XOBFVG item; idx = GetOldest(source); has = IsValidIndex(idx); if (!has) { // item.Clean(); last.Clean(); break; } item = source[idx]; ArrayRemove( source, idx, 1 // ); // bool isAbove = IsAbove( item.ob, last.ob, ignoreInside // ); // bool isBelow = IsBelow( item.ob, last.ob, ignoreInside // ); // has = isBullish ? isAbove : isBelow; if (has) { // AddRef( item, tmpItems // ); // last = item; hasLast = item.IsValid(); } } // Clean(source); Copy( tmpItems, source // ); // Clean(tmpItems); } //