Files
Bell-PriceActionWithEma-EA/Include/ICT2026/Swing.mqh
T

332 lines
8.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| Swing.mqh — pivot & swing set H0L1 (tham khảo HyperICT) |
//+------------------------------------------------------------------+
#ifndef ICT2026_SWING_MQH
#define ICT2026_SWING_MQH
#include <ICT2026/Types.mqh>
bool IctIsSwingHigh(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const int range)
{
const int str = MathMax(1, range);
const double h = iHigh(sym, tf, shift);
for(int k = 1; k <= str; k++)
{
if(shift + k >= Bars(sym, tf) || shift - k < 0)
return false;
if(h <= iHigh(sym, tf, shift + k) || h <= iHigh(sym, tf, shift - k))
return false;
}
return true;
}
bool IctIsSwingLow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const int range)
{
const int str = MathMax(1, range);
const double l = iLow(sym, tf, shift);
for(int k = 1; k <= str; k++)
{
if(shift + k >= Bars(sym, tf) || shift - k < 0)
return false;
if(l >= iLow(sym, tf, shift + k) || l >= iLow(sym, tf, shift - k))
return false;
}
return true;
}
void IctSortSwingsByTime(IctSwingPoint &pts[], const int count)
{
for(int i = 0; i < count - 1; i++)
for(int j = i + 1; j < count; j++)
if(pts[j].time < pts[i].time)
{
const IctSwingPoint t = pts[i];
pts[i] = pts[j];
pts[j] = t;
}
}
void IctCollectSwings(const string sym, const ENUM_TIMEFRAMES tf,
const int range, const int lookback,
IctSwingPoint &highs[], IctSwingPoint &lows[])
{
ArrayResize(highs, 0);
ArrayResize(lows, 0);
const int str = MathMax(1, range);
const int bars = Bars(sym, tf);
const int last = MathMin(lookback, bars - str - 1);
if(last < str + 5)
return;
int nH = 0, nL = 0;
for(int sh = last; sh >= 1; sh--)
{
if(IctIsSwingHigh(sym, tf, sh, range))
{
ArrayResize(highs, nH + 1);
highs[nH].price = iHigh(sym, tf, sh);
highs[nH].time = iTime(sym, tf, sh);
highs[nH].shift = sh;
nH++;
}
if(IctIsSwingLow(sym, tf, sh, range))
{
ArrayResize(lows, nL + 1);
lows[nL].price = iLow(sym, tf, sh);
lows[nL].time = iTime(sym, tf, sh);
lows[nL].shift = sh;
nL++;
}
}
IctSortSwingsByTime(highs, nH);
IctSortSwingsByTime(lows, nL);
}
IctSwingPoint IctFindMostRecentOlder(IctSwingPoint &pts[], const int count,
const int anchorShift)
{
IctSwingPoint empty;
empty.Clear();
int best = -1;
for(int i = 0; i < count; i++)
{
if(pts[i].shift <= anchorShift)
continue;
if(best < 0 || pts[i].shift < pts[best].shift)
best = i;
}
if(best < 0)
return empty;
return pts[best];
}
IctSwingSet IctPickStructuralBear(IctSwingPoint &highs[], const int nH,
IctSwingPoint &lows[], const int nL)
{
IctSwingSet out;
out.Clear();
if(nH < 2 || nL < 2)
return out;
out.l0 = lows[nL - 1];
out.hasL0 = true;
out.l1 = IctFindMostRecentOlder(lows, nL, out.l0.shift);
if(!out.l1.Valid())
return out;
out.hasL1 = true;
out.h0 = IctFindMostRecentOlder(highs, nH, out.l1.shift);
if(!out.h0.Valid())
return out;
out.hasH0 = true;
out.h1 = IctFindMostRecentOlder(highs, nH, out.h0.shift);
if(!out.h1.Valid())
return out;
out.hasH1 = true;
return out;
}
IctSwingSet IctPickStructuralBull(IctSwingPoint &highs[], const int nH,
IctSwingPoint &lows[], const int nL)
{
IctSwingSet out;
out.Clear();
if(nH < 2 || nL < 2)
return out;
out.h0 = highs[nH - 1];
out.hasH0 = true;
out.l0 = IctFindMostRecentOlder(lows, nL, out.h0.shift);
if(!out.l0.Valid())
return out;
out.hasL0 = true;
out.h1 = IctFindMostRecentOlder(highs, nH, out.l0.shift);
if(!out.h1.Valid())
return out;
out.hasH1 = true;
out.l1 = IctFindMostRecentOlder(lows, nL, out.h1.shift);
if(!out.l1.Valid())
return out;
out.hasL1 = true;
return out;
}
ENUM_ICT_STRUCT IctClassifyStructure(IctSwingSet &sw)
{
if(!sw.IsComplete())
return ICT_STRUCT_NONE;
if(sw.h0.price > sw.h1.price && sw.l0.price > sw.l1.price)
return ICT_STRUCT_BULL;
if(sw.h0.price < sw.h1.price && sw.l0.price < sw.l1.price)
return ICT_STRUCT_BEAR;
return ICT_STRUCT_NONE;
}
bool IctIsPullbackValid(IctSwingSet &sw, const double ratio)
{
if(!sw.IsComplete() || ratio <= 0.0)
return true;
const double impulse = MathAbs(sw.h1.price - sw.l1.price);
const double pullback = MathAbs(sw.h1.price - sw.l0.price);
if(impulse <= 0.0)
return false;
return pullback >= impulse * ratio;
}
IctSwingSet IctBuildSwingSet(const string sym, const ENUM_TIMEFRAMES tf,
const int range, const int lookback,
ENUM_ICT_STRUCT &biasOut)
{
IctSwingSet out;
out.Clear();
biasOut = ICT_STRUCT_NONE;
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, tf, range, lookback, highs, lows);
const int nH = ArraySize(highs);
const int nL = ArraySize(lows);
IctSwingSet bear = IctPickStructuralBear(highs, nH, lows, nL);
IctSwingSet bull = IctPickStructuralBull(highs, nH, lows, nL);
const bool hasBear = bear.IsComplete();
const bool hasBull = bull.IsComplete();
const ENUM_ICT_STRUCT biasBear = hasBear ? IctClassifyStructure(bear) : ICT_STRUCT_NONE;
const ENUM_ICT_STRUCT biasBull = hasBull ? IctClassifyStructure(bull) : ICT_STRUCT_NONE;
if(biasBear == ICT_STRUCT_BEAR)
{
out = bear;
biasOut = ICT_STRUCT_BEAR;
}
else if(biasBull == ICT_STRUCT_BULL)
{
out = bull;
biasOut = ICT_STRUCT_BULL;
}
else if(hasBear)
{
out = bear;
biasOut = IctClassifyStructure(bear);
}
else if(hasBull)
{
out = bull;
biasOut = IctClassifyStructure(bull);
}
return out;
}
// Intraday: 2 đỉnh + 2 đáy gần nhất (mới hơn = phần tử cuối sau sort time)
bool IctTakeLastTwoSwings(IctSwingPoint &pts[], const int count,
IctSwingPoint &older, IctSwingPoint &newer)
{
older.Clear();
newer.Clear();
if(count < 2)
return false;
older = pts[count - 2];
newer = pts[count - 1];
return older.Valid() && newer.Valid();
}
int IctFilterSwingsByMaxShift(IctSwingPoint &pts[], const int count, const int maxShift)
{
if(maxShift <= 0 || count < 1)
return count;
int w = 0;
for(int i = 0; i < count; i++)
{
if(pts[i].shift > maxShift)
continue;
if(w != i)
pts[w] = pts[i];
w++;
}
if(w < count)
ArrayResize(pts, w);
return w;
}
// Phân loại HH-HL / LH-LL từ pivot gần nhất — tránh ghép leg 4 swing cũ (sai trên H1)
bool IctBuildSwingSetRecentPivots(const string sym, const ENUM_TIMEFRAMES tf,
const int range, const int lookback,
const int maxRecentShift,
IctSwingSet &out, ENUM_ICT_STRUCT &biasOut)
{
out.Clear();
biasOut = ICT_STRUCT_NONE;
IctSwingPoint highs[], lows[];
IctCollectSwings(sym, tf, range, lookback, highs, lows);
int nH = ArraySize(highs);
int nL = ArraySize(lows);
if(maxRecentShift > 0)
{
nH = IctFilterSwingsByMaxShift(highs, nH, maxRecentShift);
nL = IctFilterSwingsByMaxShift(lows, nL, maxRecentShift);
}
IctSwingPoint hOld, hNew, lOld, lNew;
if(!IctTakeLastTwoSwings(highs, nH, hOld, hNew))
return false;
if(!IctTakeLastTwoSwings(lows, nL, lOld, lNew))
return false;
out.h0 = hNew;
out.hasH0 = true;
out.h1 = hOld;
out.hasH1 = true;
out.l0 = lNew;
out.hasL0 = true;
out.l1 = lOld;
out.hasL1 = true;
const bool hh = hNew.price > hOld.price + _Point;
const bool hl = lNew.price > lOld.price + _Point;
const bool lh = hNew.price < hOld.price - _Point;
const bool ll = lNew.price < lOld.price - _Point;
if(hh && hl)
biasOut = ICT_STRUCT_BULL;
else if(lh && ll)
biasOut = ICT_STRUCT_BEAR;
else
biasOut = ICT_STRUCT_NONE;
return out.IsComplete();
}
void IctGetKeyLevels(const ENUM_ICT_STRUCT bias, IctSwingSet &sw,
double &keyLv1, double &keyLv2)
{
keyLv1 = 0.0;
keyLv2 = 0.0;
if(!sw.IsComplete())
return;
if(bias == ICT_STRUCT_BULL)
{
keyLv1 = sw.l0.price;
keyLv2 = sw.h0.price;
}
else if(bias == ICT_STRUCT_BEAR)
{
keyLv1 = sw.h0.price;
keyLv2 = sw.l0.price;
}
}
#endif