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

368 lines
14 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.
//+------------------------------------------------------------------+
//| UpdateEngine.mqh |
//| §1.4 CHoCH & Trend Continue — cập nhật incremental |
//+------------------------------------------------------------------+
//| ĐÃ GIẢI QUYẾT: |
//| • Chỉ chạy khi nến HTF mới đóng; không quét lại full lookback |
//| • Phá Key LV1 (body) → CHoCH | Phá Key LV2 → Continue |
//| • §1.4a Bull Continue P1: track newH0 (nến xanh), newL0 (đáy) |
//| • §1.4a P2: IsConfirmedSwingHigh(newH0) → roll, khóa newH0 |
//| • §1.4b Bull CHoCH P12: track newL0 → swing low confirm → P3 |
//| • §1.4b P3 case1: phá H0 → newH0 confirm → roll (L1 giữ) |
//| • §1.4b P3 case2: phá newL0 → newH0+newL02 confirm → bear roll|
//| • §1.4c/d Bear: đối xứng Continue & CHoCH |
//| • Xác nhận đỉnh/đáy = InpSwingRange (KHÔNG dùng Fib ở đây) |
//| CHƯA TỐI ƯU: gắn OB chính xác cho newL0 mỗi leg; edge case đa tín hiệu |
//+------------------------------------------------------------------+
#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;
}
//--- Phát hiện body phá Key LV1/L2 lần đầu → bật BUILD
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");
}
}
}
//--- §1.4a Bull Continue — P1 track + P2 swing high confirm → roll
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");
}
//--- §1.4b Bull CHoCH — P1 track newL0, P2 swing low confirm → CHOCH_RESOLVE
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");
}
//--- §1.4b Bull CHoCH P3 — case1 về bull / case2 confirm bear
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