Define HTF Market Structure, KeyLevels

This commit is contained in:
Bell
2026-05-17 23:03:18 +07:00
parent 12f9e82a00
commit 5c90171709
14 changed files with 1380 additions and 1 deletions
-1
View File
@@ -1,5 +1,4 @@
Profiles/
Experts/*.ex5
*.dat
Include/
Logs/*.log
+55
View File
@@ -0,0 +1,55 @@
//+------------------------------------------------------------------+
//| HyperICT.mq5 |
//| HTF structure: swings, Key LV, CHoCH / Continue (modular) |
//+------------------------------------------------------------------+
#property copyright "HyperICT"
#property version "0.11"
#property description "HTF structure | Fib=init only | pivot confirm=InpSwingRange"
#include <HyperICT/Config.mqh>
#include <HyperICT/Types.mqh>
#include <HyperICT/StateMachine.mqh>
#include <HyperICT/Draw.mqh>
#include <HyperICT/Panel.mqh>
HtfContext g_ctx;
//+------------------------------------------------------------------+
int OnInit()
{
g_ctx.symbol = _Symbol;
g_ctx.htf = InpHtf;
g_ctx.lastHtfBar = iTime(_Symbol, InpHtf, 0);
if(!CStateMachine::Init(g_ctx, _Symbol))
{
Print("[HyperICT] Init: chưa đủ swing — chờ dữ liệu hoặc giảm InpSwingRange");
return INIT_SUCCEEDED;
}
CDraw::Render(g_ctx);
CPanel::Render(g_ctx);
ChartRedraw();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
CDraw::ClearAll();
CPanel::Clear();
ChartRedraw();
}
//+------------------------------------------------------------------+
void OnTick()
{
CStateMachine::OnChartEvent(g_ctx);
CDraw::Render(g_ctx);
CPanel::Render(g_ctx);
}
//+------------------------------------------------------------------+
ENUM_HTF_STATE HyperICT_GetState() { return g_ctx.state; }
//+------------------------------------------------------------------+
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
//+------------------------------------------------------------------+
//| Classifier.mqh — 7+ trạng thái §1.1 (bar đóng shift 1) |
//+------------------------------------------------------------------+
#ifndef HYPERICT_CLASSIFIER_MQH
#define HYPERICT_CLASSIFIER_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
#include <HyperICT/FibValidator.mqh>
#include <HyperICT/KeyLevels.mqh>
//+------------------------------------------------------------------+
class CClassifier
{
public:
static ENUM_HTF_STATE Evaluate(HtfContext &ctx)
{
if(!ctx.snapshotReady || !ctx.swings.IsComplete())
return HTF_NEUTRAL;
if(ctx.structBias == STRUCT_NONE)
return HTF_NEUTRAL;
const int sh = 1;
const double bodyMid = (iClose(ctx.symbol, ctx.htf, sh) +
iOpen(ctx.symbol, ctx.htf, sh)) * 0.5;
if(ctx.update.phase != UPD_PHASE_IDLE)
{
if(ctx.structBias == STRUCT_BULL)
{
if(ctx.update.event == UPD_CONTINUE)
return HTF_BULL_CONTINUE;
if(ctx.update.event == UPD_CHOCH)
return HTF_BULL_CHOCH;
}
else if(ctx.structBias == STRUCT_BEAR)
{
if(ctx.update.event == UPD_CONTINUE)
return HTF_BEAR_CONTINUE;
if(ctx.update.event == UPD_CHOCH)
return HTF_BEAR_CHOCH;
}
}
if(!ctx.fibOk)
{
if(ctx.structBias == STRUCT_BULL)
return HTF_BULL_INCOMPLETE;
if(ctx.structBias == STRUCT_BEAR)
return HTF_BEAR_INCOMPLETE;
}
if(ctx.structBias == STRUCT_BULL)
{
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv1.price))
return HTF_BULL_CHOCH;
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
return HTF_BULL_CONTINUE;
if(CKeyLevels::PriceBetween(bodyMid, ctx.keyLv1.price, ctx.keyLv2.price))
return HTF_BULL_PULLBACK;
}
else if(ctx.structBias == STRUCT_BEAR)
{
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv1.price))
return HTF_BEAR_CHOCH;
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
return HTF_BEAR_CONTINUE;
if(CKeyLevels::PriceBetween(bodyMid, ctx.keyLv1.price, ctx.keyLv2.price))
return HTF_BEAR_PULLBACK;
}
return HTF_NEUTRAL;
}
static string StateText(const ENUM_HTF_STATE s)
{
switch(s)
{
case HTF_BULL_INCOMPLETE: return "BULL (Fib pending)";
case HTF_BEAR_INCOMPLETE: return "BEAR (Fib pending)";
case HTF_BULL_PULLBACK: return "BULL PULLBACK";
case HTF_BULL_CHOCH: return "BULL CHoCH";
case HTF_BULL_CONTINUE: return "BULL CONTINUE";
case HTF_BEAR_PULLBACK: return "BEAR PULLBACK";
case HTF_BEAR_CHOCH: return "BEAR CHoCH";
case HTF_BEAR_CONTINUE: return "BEAR CONTINUE";
default: return "NEUTRAL";
}
}
};
#endif // HYPERICT_CLASSIFIER_MQH
+35
View File
@@ -0,0 +1,35 @@
//+------------------------------------------------------------------+
//| Config.mqh — inputs & hằng số |
//+------------------------------------------------------------------+
#ifndef HYPERICT_CONFIG_MQH
#define HYPERICT_CONFIG_MQH
#include <HyperICT/Types.mqh>
input group "══ HTF Structure ══"
input ENUM_TIMEFRAMES InpHtf = PERIOD_H1;
input int InpSwingRange = 3;
input int InpSwingLookback = 400;
input double InpFibMinRatio = 0.382;
input group "══ Hiển thị ══"
input bool InpDrawSwings = true;
input bool InpDrawZones = true;
input bool InpDrawPanel = true;
input uchar InpZoneFillAlpha = 72;
input color InpClrBullHi = clrDodgerBlue;
input color InpClrBullLo = clrDeepSkyBlue;
input color InpClrBearHi = clrOrangeRed;
input color InpClrBearLo = clrGold;
input color InpClrSupply = C'140,50,55';
input color InpClrDemand = C'35,110,55';
input int InpLabelFontSize = 9;
input int InpPanelX = 12;
input int InpPanelY = 28;
input group "══ Debug ══"
input bool InpDebug = false;
const string HICT_OBJ_PFX = "HICT_";
#endif // HYPERICT_CONFIG_MQH
+126
View File
@@ -0,0 +1,126 @@
//+------------------------------------------------------------------+
//| Draw.mqh — labels H0L1, vùng Key LV1/L2 |
//+------------------------------------------------------------------+
#ifndef HYPERICT_DRAW_MQH
#define HYPERICT_DRAW_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
#include <HyperICT/KeyLevels.mqh>
//+------------------------------------------------------------------+
class CDraw
{
static long Chart() { return ChartID(); }
static color WithAlpha(const color clr, const uchar alpha)
{
return (color)((long)clr | ((long)alpha << 24));
}
static void DeleteByPrefix(const string prefix)
{
const long ch = Chart();
for(int i = ObjectsTotal(ch, 0, -1) - 1; i >= 0; i--)
{
const string name = ObjectName(ch, i, 0, -1);
if(StringFind(name, prefix) == 0)
ObjectDelete(ch, name);
}
}
static void DrawSwingLabel(const string id, const string text,
const datetime t, const double price,
const bool isHigh, const color clr)
{
const long ch = Chart();
if(ObjectFind(ch, id) < 0)
ObjectCreate(ch, id, OBJ_TEXT, 0, t, price);
ObjectMove(ch, id, 0, t, price);
ObjectSetString(ch, id, OBJPROP_TEXT, text);
ObjectSetInteger(ch, id, OBJPROP_COLOR, clr);
ObjectSetInteger(ch, id, OBJPROP_FONTSIZE, InpLabelFontSize);
ObjectSetString(ch, id, OBJPROP_FONT, "Arial Bold");
ObjectSetInteger(ch, id, OBJPROP_ANCHOR, isHigh ? ANCHOR_BOTTOM : ANCHOR_TOP);
ObjectSetInteger(ch, id, OBJPROP_SELECTABLE, false);
}
static void DrawZoneRect(const string id,
const datetime t1, const double top, const double bottom,
const color fillClr, const string tip)
{
const long ch = Chart();
const datetime t2 = iTime(_Symbol, PERIOD_CURRENT, 0);
if(t2 == 0) return;
if(ObjectFind(ch, id) < 0)
ObjectCreate(ch, id, OBJ_RECTANGLE, 0, t1, top, t2, bottom);
ObjectMove(ch, id, 0, t1, top);
ObjectMove(ch, id, 1, t2, bottom);
ObjectSetInteger(ch, id, OBJPROP_COLOR, fillClr);
ObjectSetInteger(ch, id, OBJPROP_FILL, true);
ObjectSetInteger(ch, id, OBJPROP_BACK, true);
ObjectSetInteger(ch, id, OBJPROP_SELECTABLE, false);
ObjectSetString(ch, id, OBJPROP_TOOLTIP, tip);
}
public:
static void ClearAll()
{
DeleteByPrefix(HICT_OBJ_PFX);
}
static void Render(HtfContext &ctx)
{
if(!ctx.snapshotReady)
return;
const string p = HICT_OBJ_PFX;
if(InpDrawSwings && ctx.swings.IsComplete())
{
DrawSwingLabel(p + "H0", "H0", ctx.swings.h0.time, ctx.swings.h0.price, true, InpClrBullHi);
DrawSwingLabel(p + "H1", "H1", ctx.swings.h1.time, ctx.swings.h1.price, true, InpClrBullHi);
DrawSwingLabel(p + "L0", "L0", ctx.swings.l0.time, ctx.swings.l0.price, false, InpClrBullLo);
DrawSwingLabel(p + "L1", "L1", ctx.swings.l1.time, ctx.swings.l1.price, false, InpClrBullLo);
if(ctx.update.newH0.Valid())
DrawSwingLabel(p + "nH0", "newH0", ctx.update.newH0.time,
ctx.update.newH0.price, true, clrYellow);
if(ctx.update.newL0.Valid())
DrawSwingLabel(p + "nL0", "newL0", ctx.update.newL0.time,
ctx.update.newL0.price, false, clrYellow);
if(ctx.update.newL02.Valid())
DrawSwingLabel(p + "nL2", "newL02", ctx.update.newL02.time,
ctx.update.newL02.price, false, clrOrange);
}
if(!InpDrawZones)
return;
double top, bot;
datetime tOb;
if(ctx.keyLv1.zone.valid)
{
tOb = ctx.keyLv1.zone.obTime;
const bool supply = (ctx.structBias == STRUCT_BULL);
CKeyLevels::ZoneRectBounds(ctx.keyLv1.zone, supply, top, bot);
DrawZoneRect(p + "Z_K1", tOb, top, bot,
WithAlpha(supply ? InpClrSupply : InpClrDemand, InpZoneFillAlpha),
"Key LV1");
}
if(ctx.keyLv2.zone.valid)
{
tOb = ctx.keyLv2.zone.obTime;
const bool supply = (ctx.structBias == STRUCT_BEAR);
CKeyLevels::ZoneRectBounds(ctx.keyLv2.zone, supply, top, bot);
DrawZoneRect(p + "Z_K2", tOb, top, bot,
WithAlpha(supply ? InpClrSupply : InpClrDemand, InpZoneFillAlpha),
"Key LV2");
}
}
};
#endif // HYPERICT_DRAW_MQH
+31
View File
@@ -0,0 +1,31 @@
//+------------------------------------------------------------------+
//| FibValidator.mqh — Fib 0.382 chỉ cho tiên quyết trend ban đầu |
//+------------------------------------------------------------------+
#ifndef HYPERICT_FIBVALIDATOR_MQH
#define HYPERICT_FIBVALIDATOR_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
class CFibValidator
{
public:
static double LegSize(const double from, const double to)
{
return MathAbs(to - from);
}
// Sóng hồi H1→L0 >= ratio × sóng L1→H1 (bull & bear, chỉ lúc lock trend)
static bool IsPullbackValid(const SwingSet &sw, const double ratio)
{
if(!sw.IsComplete())
return false;
const double impulse = LegSize(sw.l1.price, sw.h1.price);
const double pullback = LegSize(sw.h1.price, sw.l0.price);
if(impulse <= 0.0)
return false;
return pullback >= impulse * ratio;
}
};
#endif // HYPERICT_FIBVALIDATOR_MQH
+159
View File
@@ -0,0 +1,159 @@
//+------------------------------------------------------------------+
//| KeyLevels.mqh — Key LV1/L2, supply/demand OB (body break) |
//+------------------------------------------------------------------+
#ifndef HYPERICT_KEYLEVELS_MQH
#define HYPERICT_KEYLEVELS_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
//+------------------------------------------------------------------+
class CKeyLevels
{
public:
static double BodyTop(const string sym, const ENUM_TIMEFRAMES tf, const int shift)
{
return MathMax(iOpen(sym, tf, shift), iClose(sym, tf, shift));
}
static double BodyBottom(const string sym, const ENUM_TIMEFRAMES tf, const int shift)
{
return MathMin(iOpen(sym, tf, shift), iClose(sym, tf, shift));
}
static bool IsBearCandle(const string sym, const ENUM_TIMEFRAMES tf, const int shift)
{
return iClose(sym, tf, shift) < iOpen(sym, tf, shift);
}
static bool IsBullCandle(const string sym, const ENUM_TIMEFRAMES tf, const int shift)
{
return iClose(sym, tf, shift) > iOpen(sym, tf, shift);
}
// Nến đỏ cuối cùng trước/at swing low (supply @ L0)
static bool FindSupplyOB(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &swingLow,
OrderBlockZone &out)
{
out.Clear();
if(swingLow.shift < 1)
return false;
const int from = swingLow.shift + 8;
const int to = MathMax(1, swingLow.shift - 1);
for(int sh = from; sh >= to; sh--)
{
if(!IsBearCandle(sym, tf, sh))
continue;
const double l = iLow(sym, tf, sh);
if(l > swingLow.price + 20 * _Point)
continue;
out.valid = true;
out.swingPrice = swingLow.price;
out.obHigh = iHigh(sym, tf, sh);
out.obLow = iLow(sym, tf, sh);
out.obTime = iTime(sym, tf, sh);
out.obShift = sh;
return true;
}
return false;
}
// Nến xanh cuối cùng tại swing high (demand @ H0)
static bool FindDemandOB(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &swingHigh,
OrderBlockZone &out)
{
out.Clear();
if(swingHigh.shift < 1)
return false;
const int from = swingHigh.shift + 8;
const int to = MathMax(1, swingHigh.shift - 1);
for(int sh = from; sh >= to; sh--)
{
if(!IsBullCandle(sym, tf, sh))
continue;
const double h = iHigh(sym, tf, sh);
if(h < swingHigh.price - 20 * _Point)
continue;
out.valid = true;
out.swingPrice = swingHigh.price;
out.obHigh = iHigh(sym, tf, sh);
out.obLow = iLow(sym, tf, sh);
out.obTime = iTime(sym, tf, sh);
out.obShift = sh;
return true;
}
return false;
}
static void BuildKeyLevels(const string sym, const ENUM_TIMEFRAMES tf,
const ENUM_STRUCT_BIAS bias,
const SwingSet &sw,
KeyLevelPair &lv1, KeyLevelPair &lv2)
{
lv1.zone.Clear();
lv2.zone.Clear();
if(!sw.IsComplete())
return;
if(bias == STRUCT_BULL)
{
lv1.price = sw.l0.price;
lv2.price = sw.h0.price;
FindSupplyOB(sym, tf, sw.l0, lv1.zone);
FindDemandOB(sym, tf, sw.h0, lv2.zone);
}
else if(bias == STRUCT_BEAR)
{
lv1.price = sw.h0.price;
lv2.price = sw.l0.price;
FindDemandOB(sym, tf, sw.h0, lv1.zone); // Key LV1 bear = H0 (demand side OB at H0)
FindSupplyOB(sym, tf, sw.l0, lv2.zone);
}
}
static bool BodyBreakBelow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const double level)
{
return BodyBottom(sym, tf, shift) < level - _Point;
}
static bool BodyBreakAbove(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const double level)
{
return BodyTop(sym, tf, shift) > level + _Point;
}
static bool PriceBetween(const double price, const double a, const double b)
{
const double lo = MathMin(a, b);
const double hi = MathMax(a, b);
return price >= lo - _Point && price <= hi + _Point;
}
static void ZoneRectBounds(const OrderBlockZone &z,
const bool supplyAtLow,
double &top, double &bottom)
{
if(!z.valid)
{
top = bottom = z.swingPrice;
return;
}
if(supplyAtLow)
{
top = MathMax(z.swingPrice, z.obHigh);
bottom = MathMin(z.swingPrice, z.obLow);
}
else
{
top = MathMax(z.swingPrice, z.obHigh);
bottom = MathMin(z.swingPrice, z.obLow);
}
}
};
#endif // HYPERICT_KEYLEVELS_MQH
+64
View File
@@ -0,0 +1,64 @@
//+------------------------------------------------------------------+
//| Panel.mqh — nhãn trạng thái góc chart |
//+------------------------------------------------------------------+
#ifndef HYPERICT_PANEL_MQH
#define HYPERICT_PANEL_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
#include <HyperICT/Classifier.mqh>
//+------------------------------------------------------------------+
class CPanel
{
public:
static void Clear()
{
ObjectDelete(ChartID(), HICT_OBJ_PFX + "PANEL");
}
static void Render(HtfContext &ctx)
{
if(!InpDrawPanel)
{
Clear();
return;
}
const long ch = ChartID();
const string name = HICT_OBJ_PFX + "PANEL";
string bias = "—";
if(ctx.structBias == STRUCT_BULL) bias = "HH-HL";
else if(ctx.structBias == STRUCT_BEAR) bias = "LH-LL";
string txt = StringFormat("HyperICT | %s %s\n", ctx.symbol, EnumToString(ctx.htf));
txt += StringFormat("Structure: %s | Fib: %s\n", bias, ctx.fibOk ? "OK" : "pending");
txt += "State: " + CClassifier::StateText(ctx.state) + "\n";
if(ctx.swings.IsComplete())
{
txt += StringFormat("H0=%.5f L0=%.5f | Key1=%.5f Key2=%.5f",
ctx.swings.h0.price, ctx.swings.l0.price,
ctx.keyLv1.price, ctx.keyLv2.price);
}
if(ctx.update.phase != UPD_PHASE_IDLE)
txt += StringFormat("\nUpdate: evt=%d phase=%d | swingR=%d",
ctx.update.event, ctx.update.phase, InpSwingRange);
if(ObjectFind(ch, name) < 0)
{
ObjectCreate(ch, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(ch, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(ch, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
ObjectSetString(ch, name, OBJPROP_FONT, "Consolas");
ObjectSetInteger(ch, name, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(ch, name, OBJPROP_XDISTANCE, InpPanelX);
ObjectSetInteger(ch, name, OBJPROP_YDISTANCE, InpPanelY);
ObjectSetInteger(ch, name, OBJPROP_FONTSIZE, InpLabelFontSize + 1);
ObjectSetInteger(ch, name, OBJPROP_COLOR, clrWhite);
ObjectSetString(ch, name, OBJPROP_TEXT, txt);
}
};
#endif // HYPERICT_PANEL_MQH
+71
View File
@@ -0,0 +1,71 @@
//+------------------------------------------------------------------+
//| StateMachine.mqh — pipeline đánh giá HTF mỗi bar |
//+------------------------------------------------------------------+
#ifndef HYPERICT_STATEMACHINE_MQH
#define HYPERICT_STATEMACHINE_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
#include <HyperICT/SwingEngine.mqh>
#include <HyperICT/FibValidator.mqh>
#include <HyperICT/KeyLevels.mqh>
#include <HyperICT/Classifier.mqh>
#include <HyperICT/UpdateEngine.mqh>
//+------------------------------------------------------------------+
class CStateMachine
{
static void Dbg(const string msg)
{
if(InpDebug)
Print("[HyperICT] ", msg);
}
static void RefreshDerived(HtfContext &ctx)
{
// Fib 0.382 chỉ kiểm tra tiên quyết trên bộ swing hiện tại (không dùng trong §1.4)
ctx.fibOk = CFibValidator::IsPullbackValid(ctx.swings, InpFibMinRatio);
CKeyLevels::BuildKeyLevels(ctx.symbol, ctx.htf, ctx.structBias,
ctx.swings, ctx.keyLv1, ctx.keyLv2);
ctx.state = CClassifier::Evaluate(ctx);
}
public:
static bool Init(HtfContext &ctx, const string sym)
{
ctx.symbol = sym;
ctx.htf = InpHtf;
ctx.update.Clear();
ctx.lastHtfBar = 0;
ctx.snapshotReady = false;
if(!CSwingEngine::LockInitialSnapshot(ctx))
{
Dbg("Init fail: không đủ 4 swing HTF");
return false;
}
ctx.structBias = CSwingEngine::ClassifyStructure(ctx.swings);
RefreshDerived(ctx);
Dbg(StringFormat("Snapshot locked | bias=%d fib=%s state=%s",
ctx.structBias, ctx.fibOk ? "OK" : "pending",
CClassifier::StateText(ctx.state)));
return true;
}
static void OnChartEvent(HtfContext &ctx, const bool forceRedraw = false)
{
const bool newBar = CUpdateEngine::IsNewHtfBar(ctx);
if(newBar)
{
ctx.lastHtfBar = iTime(ctx.symbol, ctx.htf, 0);
CUpdateEngine::OnHtfBarClose(ctx);
RefreshDerived(ctx);
if(InpDebug)
Dbg("HTF bar → " + CClassifier::StateText(ctx.state));
}
else if(forceRedraw)
RefreshDerived(ctx);
}
};
#endif // HYPERICT_STATEMACHINE_MQH
+257
View File
@@ -0,0 +1,257 @@
//+------------------------------------------------------------------+
//| SwingEngine.mqh — pivot, H0L1, snapshot khóa |
//+------------------------------------------------------------------+
#ifndef HYPERICT_SWINGENGINE_MQH
#define HYPERICT_SWINGENGINE_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
//+------------------------------------------------------------------+
class CSwingEngine
{
public:
static bool IsSwingHigh(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;
}
static bool IsSwingLow(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;
}
static int MinConfirmShift(const int range)
{
return MathMax(1, range) + 1;
}
static bool CanConfirmPivot(const int pivotShift, const int range)
{
return pivotShift >= MinConfirmShift(range);
}
static void FillSwingHigh(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &out)
{
out.price = iHigh(sym, tf, shift);
out.time = iTime(sym, tf, shift);
out.shift = shift;
}
static void FillSwingLow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &out)
{
out.price = iLow(sym, tf, shift);
out.time = iTime(sym, tf, shift);
out.shift = shift;
}
static bool IsConfirmedSwingHigh(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &pt, const int range)
{
if(!pt.Valid() || !CanConfirmPivot(pt.shift, range))
return false;
return IsSwingHigh(sym, tf, pt.shift, range);
}
static bool IsConfirmedSwingLow(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &pt, const int range)
{
if(!pt.Valid() || !CanConfirmPivot(pt.shift, range))
return false;
return IsSwingLow(sym, tf, pt.shift, range);
}
static void SortByTime(SwingPoint &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 SwingPoint t = pts[i];
pts[i] = pts[j];
pts[j] = t;
}
}
static void CollectSwings(const string sym, const ENUM_TIMEFRAMES tf,
const int range, const int lookback,
SwingPoint &highs[], SwingPoint &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(IsSwingHigh(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(IsSwingLow(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++;
}
}
SortByTime(highs, nH);
SortByTime(lows, nL);
}
static void PickLastTwo(const SwingPoint &pts[], const int count,
SwingPoint &s0, SwingPoint &s1,
bool &has0, bool &has1)
{
has0 = has1 = false;
if(count < 1) return;
s0 = pts[count - 1];
has0 = true;
if(count < 2) return;
s1 = pts[count - 2];
has1 = true;
}
static ENUM_STRUCT_BIAS ClassifyStructure(const SwingSet &sw)
{
if(!sw.IsComplete())
return STRUCT_NONE;
if(sw.h0.price > sw.h1.price && sw.l0.price > sw.l1.price)
return STRUCT_BULL;
if(sw.h0.price < sw.h1.price && sw.l0.price < sw.l1.price)
return STRUCT_BEAR;
return STRUCT_NONE;
}
static bool BuildSwingSet(const string sym, const ENUM_TIMEFRAMES tf,
const int range, const int lookback,
SwingSet &out)
{
out.Clear();
SwingPoint highs[], lows[];
CollectSwings(sym, tf, range, lookback, highs, lows);
PickLastTwo(highs, ArraySize(highs), out.h0, out.h1, out.hasH0, out.hasH1);
PickLastTwo(lows, ArraySize(lows), out.l0, out.l1, out.hasL0, out.hasL1);
return out.IsComplete();
}
static bool LockInitialSnapshot(HtfContext &ctx)
{
ctx.swings.Clear();
if(!BuildSwingSet(ctx.symbol, ctx.htf, InpSwingRange, InpSwingLookback, ctx.swings))
return false;
ctx.structBias = ClassifyStructure(ctx.swings);
ctx.lockedAt = TimeCurrent();
ctx.snapshotReady = true;
return true;
}
static void RollBullContinue(SwingSet &sw, const SwingPoint &newH0, const SwingPoint &newL0)
{
sw.h1 = sw.h0;
sw.hasH1 = sw.hasH0;
sw.l1 = sw.l0;
sw.hasL1 = sw.hasL0;
sw.h0 = newH0;
sw.hasH0 = true;
sw.l0 = newL0;
sw.hasL0 = true;
}
static void RollBullChochCase1(SwingSet &sw, const SwingPoint &newH0, const SwingPoint &newL0)
{
sw.h1 = sw.h0;
sw.hasH1 = sw.hasH0;
// L1 giữ nguyên (spec)
sw.l0 = newL0;
sw.hasL0 = true;
sw.h0 = newH0;
sw.hasH0 = true;
}
static void RollBullChochCase2(SwingSet &sw,
const SwingPoint &newH0,
const SwingPoint &newL0,
const SwingPoint &newL02)
{
sw.h1 = sw.h0;
sw.hasH1 = sw.hasH0;
sw.h0 = newH0;
sw.hasH0 = true;
sw.l1 = newL0;
sw.hasL1 = true;
sw.l0 = newL02;
sw.hasL0 = true;
}
static void RollBearContinue(SwingSet &sw, const SwingPoint &newH0, const SwingPoint &newL0)
{
sw.l1 = sw.l0;
sw.hasL1 = sw.hasL0;
sw.h1 = sw.h0;
sw.hasH1 = sw.hasH0;
sw.l0 = newL0;
sw.hasL0 = true;
sw.h0 = newH0;
sw.hasH0 = true;
}
static void RollBearChochCase1(SwingSet &sw, const SwingPoint &newH0, const SwingPoint &newL0)
{
sw.l1 = sw.l0;
sw.hasL1 = sw.hasL0;
sw.h0 = newH0;
sw.hasH0 = true;
sw.l0 = newL0;
sw.hasL0 = true;
}
static void RollBearChochCase2(SwingSet &sw,
const SwingPoint &newH0,
const SwingPoint &newL0,
const SwingPoint &newH02)
{
sw.l1 = sw.l0;
sw.hasL1 = sw.hasL0;
sw.l0 = newL0;
sw.hasL0 = true;
sw.h0 = newH02;
sw.hasH0 = true;
sw.h1 = newH0;
sw.hasH1 = true;
}
};
#endif // HYPERICT_SWINGENGINE_MQH
+138
View File
@@ -0,0 +1,138 @@
//+------------------------------------------------------------------+
//| Types.mqh — enums & structs (spec §11.4) |
//+------------------------------------------------------------------+
#ifndef HYPERICT_TYPES_MQH
#define HYPERICT_TYPES_MQH
enum ENUM_STRUCT_BIAS
{
STRUCT_NONE = 0,
STRUCT_BULL = 1,
STRUCT_BEAR = -1
};
enum ENUM_HTF_STATE
{
HTF_NEUTRAL = 0,
HTF_BULL_INCOMPLETE = 1,
HTF_BEAR_INCOMPLETE = 2,
HTF_BULL_PULLBACK = 3,
HTF_BULL_CHOCH = 4,
HTF_BULL_CONTINUE = 5,
HTF_BEAR_PULLBACK = 6,
HTF_BEAR_CHOCH = 7,
HTF_BEAR_CONTINUE = 8
};
enum ENUM_UPDATE_EVENT
{
UPD_NONE = 0,
UPD_CHOCH = 1,
UPD_CONTINUE = 2
};
enum ENUM_UPDATE_PHASE
{
UPD_PHASE_IDLE = 0,
UPD_PHASE_BUILD = 1,
UPD_PHASE_CHOCH_RESOLVE = 2
};
enum ENUM_CHOCH_BRANCH
{
CHOCH_BRANCH_NONE = 0,
CHOCH_BRANCH_BACK_TO_BULL = 1,
CHOCH_BRANCH_CONFIRM_BEAR = 2
};
struct SwingPoint
{
double price;
datetime time;
int shift;
void Clear()
{
price = 0.0;
time = 0;
shift = -1;
}
bool Valid() const { return shift >= 0 && price > 0.0; }
};
struct OrderBlockZone
{
double swingPrice;
double obHigh;
double obLow;
datetime obTime;
int obShift;
bool valid;
void Clear()
{
swingPrice = obHigh = obLow = 0.0;
obTime = 0;
obShift = -1;
valid = false;
}
};
struct KeyLevelPair
{
double price;
OrderBlockZone zone;
};
struct SwingSet
{
SwingPoint h0, h1, l0, l1;
bool hasH0, hasH1, hasL0, hasL1;
bool IsComplete() const
{
return hasH0 && hasH1 && hasL0 && hasL1;
}
void Clear()
{
h0.Clear(); h1.Clear(); l0.Clear(); l1.Clear();
hasH0 = hasH1 = hasL0 = hasL1 = false;
}
};
struct UpdateContext
{
ENUM_UPDATE_EVENT event;
ENUM_UPDATE_PHASE phase;
ENUM_CHOCH_BRANCH chochBranch;
SwingPoint newH0;
SwingPoint newL0;
SwingPoint newL02;
bool newH0Locked;
bool newLLocked;
bool case1TrackingH0;
void Clear()
{
event = UPD_NONE;
phase = UPD_PHASE_IDLE;
chochBranch = CHOCH_BRANCH_NONE;
newH0.Clear(); newL0.Clear(); newL02.Clear();
newH0Locked = newLLocked = false;
case1TrackingH0 = false;
}
};
struct HtfContext
{
string symbol;
ENUM_TIMEFRAMES htf;
SwingSet swings;
ENUM_STRUCT_BIAS structBias;
bool fibOk;
KeyLevelPair keyLv1;
KeyLevelPair keyLv2;
ENUM_HTF_STATE state;
UpdateContext update;
datetime lockedAt;
datetime lastHtfBar;
bool snapshotReady;
};
#endif // HYPERICT_TYPES_MQH
+351
View File
@@ -0,0 +1,351 @@
//+------------------------------------------------------------------+
//| UpdateEngine.mqh — §1.4: xác nhận đỉnh/đáy bằng InpSwingRange |
//| (Fib chỉ dùng lúc lock trend ban đầu — xem FibValidator) |
//+------------------------------------------------------------------+
#ifndef HYPERICT_UPDATEENGINE_MQH
#define HYPERICT_UPDATEENGINE_MQH
#include <HyperICT/Types.mqh>
#include <HyperICT/Config.mqh>
#include <HyperICT/SwingEngine.mqh>
#include <HyperICT/KeyLevels.mqh>
class CUpdateEngine
{
static void Dbg(const string msg)
{
if(InpDebug)
Print("[HyperICT/Update] ", msg);
}
static void TrackNewHigh(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &pt, const bool onlyIfLocked)
{
if(onlyIfLocked)
return;
const double h = iHigh(sym, tf, shift);
if(!pt.Valid() || h > pt.price)
CSwingEngine::FillSwingHigh(sym, tf, shift, pt);
}
static void TrackNewLow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &pt, const bool onlyIfLocked)
{
if(onlyIfLocked)
return;
const double l = iLow(sym, tf, shift);
if(!pt.Valid() || l < pt.price)
CSwingEngine::FillSwingLow(sym, tf, shift, pt);
}
static void FinalizePivotHigh(const string sym, const ENUM_TIMEFRAMES tf,
SwingPoint &pt, const int range)
{
if(pt.Valid())
CSwingEngine::FillSwingHigh(sym, tf, pt.shift, pt);
}
static void FinalizePivotLow(const string sym, const ENUM_TIMEFRAMES tf,
SwingPoint &pt, const int range)
{
if(pt.Valid())
CSwingEngine::FillSwingLow(sym, tf, pt.shift, pt);
}
static void FinishRoll(HtfContext &ctx)
{
ctx.update.Clear();
ctx.structBias = CSwingEngine::ClassifyStructure(ctx.swings);
Dbg(StringFormat("Roll xong — bias=%d", ctx.structBias));
}
public:
static bool IsNewHtfBar(HtfContext &ctx)
{
const datetime t0 = iTime(ctx.symbol, ctx.htf, 0);
return t0 != 0 && t0 != ctx.lastHtfBar;
}
static void DetectBreakEvents(HtfContext &ctx)
{
if(ctx.update.phase != UPD_PHASE_IDLE)
return;
const int sh = 1;
if(ctx.structBias == STRUCT_BULL)
{
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv1.price))
{
ctx.update.event = UPD_CHOCH;
ctx.update.phase = UPD_PHASE_BUILD;
ctx.update.newL0.Clear();
ctx.update.newH0Locked = false;
ctx.update.newLLocked = false;
Dbg("Bull CHoCH — body break L0");
}
else if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
{
ctx.update.event = UPD_CONTINUE;
ctx.update.phase = UPD_PHASE_BUILD;
ctx.update.newH0.Clear();
ctx.update.newL0.Clear();
ctx.update.newH0Locked = false;
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
Dbg("Bull Continue — body break H0");
}
}
else if(ctx.structBias == STRUCT_BEAR)
{
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv1.price))
{
ctx.update.event = UPD_CHOCH;
ctx.update.phase = UPD_PHASE_BUILD;
ctx.update.newH0.Clear();
ctx.update.newH0Locked = false;
ctx.update.newLLocked = false;
Dbg("Bear CHoCH — body break H0");
}
else if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
{
ctx.update.event = UPD_CONTINUE;
ctx.update.phase = UPD_PHASE_BUILD;
ctx.update.newH0.Clear();
ctx.update.newL0.Clear();
ctx.update.newLLocked = false;
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
Dbg("Bear Continue — body break L0");
}
}
}
static void ProcessBullContinueBuild(HtfContext &ctx, const int sh)
{
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
if(ctx.update.newH0Locked)
return;
if(!CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange))
return;
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
ctx.update.newH0Locked = true;
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
CSwingEngine::RollBullContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bull Continue — swing high confirmed, rolled");
}
static void ProcessBearContinueBuild(HtfContext &ctx, const int sh)
{
if(CKeyLevels::IsBearCandle(ctx.symbol, ctx.htf, sh))
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
if(ctx.update.newLLocked)
return;
if(!CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange))
return;
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
ctx.update.newLLocked = true;
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
CSwingEngine::RollBearContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bear Continue — swing low confirmed, rolled");
}
static void ProcessBullChochBuild(HtfContext &ctx, const int sh)
{
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
if(ctx.update.newLLocked)
return;
if(!CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange))
return;
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
ctx.update.newLLocked = true;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
Dbg("Bull CHoCH — newL0 swing confirmed → phase 3");
}
static void ProcessBearChochBuild(HtfContext &ctx, const int sh)
{
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
if(ctx.update.newH0Locked)
return;
if(!CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange))
return;
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
ctx.update.newH0Locked = true;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
Dbg("Bear CHoCH — newH0 swing confirmed → phase 3");
}
static void ProcessBullChochResolve(HtfContext &ctx, const int sh)
{
if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.swings.h0.price))
{
if(!ctx.update.case1TrackingH0)
{
ctx.update.case1TrackingH0 = true;
ctx.update.newH0.Clear();
ctx.update.newH0Locked = false;
Dbg("Bull CHoCH case1 — break H0, track newH0");
}
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
if(ctx.update.case1TrackingH0 && !ctx.update.newH0Locked &&
CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange))
{
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
ctx.update.newH0Locked = true;
CSwingEngine::RollBullChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bull CHoCH case1 — newH0 confirmed, rolled");
}
return;
}
if(ctx.update.newL0.Valid() &&
CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.update.newL0.price))
{
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
const bool hOk = CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange);
const bool lOk = CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL02, InpSwingRange);
if(hOk && lOk)
{
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange);
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL02, InpSwingRange);
CSwingEngine::RollBullChochCase2(ctx.swings,
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
FinishRoll(ctx);
Dbg("Bull CHoCH case2 — bear confirmed, rolled");
}
}
}
static void ProcessBearChochResolve(HtfContext &ctx, const int sh)
{
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.swings.l0.price))
{
if(!ctx.update.case1TrackingH0)
{
ctx.update.case1TrackingH0 = true;
ctx.update.newL0.Clear();
ctx.update.newLLocked = false;
Dbg("Bear CHoCH case1 — break L0, track newL0");
}
if(CKeyLevels::IsBearCandle(ctx.symbol, ctx.htf, sh))
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
if(ctx.update.case1TrackingH0 && !ctx.update.newLLocked &&
CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange))
{
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
ctx.update.newLLocked = true;
CSwingEngine::RollBearChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bear CHoCH case1 — newL0 confirmed, rolled");
}
return;
}
if(ctx.update.newH0.Valid() &&
CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.update.newH0.price))
{
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
const bool lOk = CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange);
const bool hOk = CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newL02, InpSwingRange);
if(lOk && hOk)
{
FinalizePivotLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange);
FinalizePivotHigh(ctx.symbol, ctx.htf, ctx.update.newL02, InpSwingRange);
CSwingEngine::RollBearChochCase2(ctx.swings,
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
FinishRoll(ctx);
Dbg("Bear CHoCH case2 — bull confirmed, rolled");
}
}
}
static void OnHtfBarClose(HtfContext &ctx)
{
const int sh = 1;
if(ctx.update.phase == UPD_PHASE_IDLE)
{
DetectBreakEvents(ctx);
if(ctx.update.phase != UPD_PHASE_BUILD)
return;
if(ctx.update.event == UPD_CONTINUE)
{
if(ctx.structBias == STRUCT_BULL)
{
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
}
else if(ctx.structBias == STRUCT_BEAR)
{
if(CKeyLevels::IsBearCandle(ctx.symbol, ctx.htf, sh))
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
}
}
else if(ctx.update.event == UPD_CHOCH)
{
if(ctx.structBias == STRUCT_BULL)
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
else
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
}
return;
}
if(ctx.update.phase == UPD_PHASE_BUILD)
{
if(ctx.update.event == UPD_CONTINUE && ctx.structBias == STRUCT_BULL)
ProcessBullContinueBuild(ctx, sh);
else if(ctx.update.event == UPD_CONTINUE && ctx.structBias == STRUCT_BEAR)
ProcessBearContinueBuild(ctx, sh);
else if(ctx.update.event == UPD_CHOCH && ctx.structBias == STRUCT_BULL)
ProcessBullChochBuild(ctx, sh);
else if(ctx.update.event == UPD_CHOCH && ctx.structBias == STRUCT_BEAR)
ProcessBearChochBuild(ctx, sh);
return;
}
if(ctx.update.phase == UPD_PHASE_CHOCH_RESOLVE)
{
if(ctx.structBias == STRUCT_BULL)
ProcessBullChochResolve(ctx, sh);
else if(ctx.structBias == STRUCT_BEAR)
ProcessBearChochResolve(ctx, sh);
}
}
};
#endif // HYPERICT_UPDATEENGINE_MQH