Files
Bell-PriceActionWithEma-EA/Include/HyperICT/UpdateEngine.mqh
T
2026-05-17 23:03:18 +07:00

352 lines
13 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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