Update all

This commit is contained in:
Bell
2026-05-25 02:01:26 +07:00
parent b9c03196f6
commit f966c5ba2c
11 changed files with 1028 additions and 197 deletions
+2 -2
View File
@@ -112,9 +112,9 @@ Gán pivot: từ L0/H0 gần nhất, lùi từng bước tới pivot cũ **kề*
#### Bull CHoCH (phá L0)
1. **P1:** Track `newL0` (low thấp hơn).
2. **P2:** `IsConfirmedSwingLow(newL0)` → vào phase 3.
2. **P2:** `IsConfirmedSwingLow(newL0)` **hoặc** hồi lên đủ `InpSwingRange` (swing high sau đáy) → khóa `newL0`, vào phase 3 (không kéo `newL0` nữa).
3. **P3 case 1:** Phá lên H0 → `newH0` confirm → roll bull (`L1` giữ).
4. **P3 case 2:** Phá xuống `newL0` `newH0` + `newL02` confirm → roll bear.
4. **P3 case 2:** Body phá xuống `newL0` đã khóa → track `newH0` (confirm) → track `newL02` (confirm tuần tự) → roll bear.
Bear Continue / CHoCH: **đối xứng** với bull.
+7 -3
View File
@@ -123,9 +123,11 @@ struct UpdateContext
SwingPoint newH0;
SwingPoint newL0;
SwingPoint newL02; // CHoCH bull case2 → roll thành L0
bool newH0Locked; // ngừng track high khi swing confirm
bool newLLocked;
bool case1TrackingH0; // CHoCH P3 case1: đang tạo đỉnh mới
bool newH0Locked;
bool newLLocked; // newL0 hoàn thiện (P2) — không kéo newL0 nữa
bool case1TrackingH0; // P3 case1: phá lên H0
bool case2Active; // P3 case2: phá xuống newL0 đã khóa
bool newH0Case2Locked; // P3 case2: newH0 (hồi) đã confirm
void Clear()
{
event = UPD_NONE;
@@ -134,6 +136,8 @@ struct UpdateContext
newH0.Clear(); newL0.Clear(); newL02.Clear();
newH0Locked = newLLocked = false;
case1TrackingH0 = false;
case2Active = false;
newH0Case2Locked = false;
}
};
+291 -110
View File
@@ -1,18 +1,5 @@
//+------------------------------------------------------------------+
//| 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 |
//| UpdateEngine.mqh — §1.4 CHoCH / Continue |
//+------------------------------------------------------------------+
#ifndef HYPERICT_UPDATEENGINE_MQH
#define HYPERICT_UPDATEENGINE_MQH
@@ -31,9 +18,9 @@ class CUpdateEngine
}
static void TrackNewHigh(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &pt, const bool onlyIfLocked)
const int shift, SwingPoint &pt, const bool frozen)
{
if(onlyIfLocked)
if(frozen)
return;
const double h = iHigh(sym, tf, shift);
if(!pt.Valid() || h > pt.price)
@@ -41,27 +28,112 @@ class CUpdateEngine
}
static void TrackNewLow(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, SwingPoint &pt, const bool onlyIfLocked)
const int shift, SwingPoint &pt, const bool frozen)
{
if(onlyIfLocked)
if(frozen)
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)
static bool ConfirmedSwingHighAt(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const int range)
{
if(pt.Valid())
CSwingEngine::FillSwingHigh(sym, tf, pt.shift, pt);
SwingPoint pt;
CSwingEngine::FillSwingHigh(sym, tf, shift, pt);
return CSwingEngine::IsConfirmedSwingHigh(sym, tf, pt, range);
}
static void FinalizePivotLow(const string sym, const ENUM_TIMEFRAMES tf,
SwingPoint &pt, const int range)
static bool ConfirmedSwingLowAt(const string sym, const ENUM_TIMEFRAMES tf,
const int shift, const int range)
{
if(pt.Valid())
CSwingEngine::FillSwingLow(sym, tf, pt.shift, pt);
SwingPoint pt;
CSwingEngine::FillSwingLow(sym, tf, shift, pt);
return CSwingEngine::IsConfirmedSwingLow(sym, tf, pt, range);
}
// P2 bull CHoCH: đáy confirm (swing low) HOẶC đã hồi lên có swing high sau đáy
static bool TryConfirmBullChochNewL0(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &newL0, const int range)
{
if(!newL0.Valid())
return false;
if(CSwingEngine::IsConfirmedSwingLow(sym, tf, newL0, range))
return true;
for(int sh = 1; sh < newL0.shift; sh++)
{
if(iLow(sym, tf, sh) <= newL0.price + _Point)
continue;
if(ConfirmedSwingHighAt(sym, tf, sh, range))
return true;
}
return false;
}
// Đỉnh hồi cao nhất (đã confirm) sau đáy newL0 — dùng seed newH0 case2
static bool FindRetraceHighAfterLow(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &low, const int range,
SwingPoint &outHigh)
{
outHigh.Clear();
if(!low.Valid())
return false;
for(int sh = 1; sh < low.shift; sh++)
{
if(iLow(sym, tf, sh) <= low.price + _Point)
continue;
if(!ConfirmedSwingHighAt(sym, tf, sh, range))
continue;
const double h = iHigh(sym, tf, sh);
if(!outHigh.Valid() || h > outHigh.price)
CSwingEngine::FillSwingHigh(sym, tf, sh, outHigh);
}
return outHigh.Valid();
}
static bool FindRetraceLowAfterHigh(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &high, const int range,
SwingPoint &outLow)
{
outLow.Clear();
if(!high.Valid())
return false;
for(int sh = 1; sh < high.shift; sh++)
{
if(iHigh(sym, tf, sh) >= high.price - _Point)
continue;
if(!ConfirmedSwingLowAt(sym, tf, sh, range))
continue;
const double l = iLow(sym, tf, sh);
if(!outLow.Valid() || l < outLow.price)
CSwingEngine::FillSwingLow(sym, tf, sh, outLow);
}
return outLow.Valid();
}
// P2 bear CHoCH: đỉnh confirm hoặc hồi xuống có swing low sau đỉnh
static bool TryConfirmBearChochNewH0(const string sym, const ENUM_TIMEFRAMES tf,
const SwingPoint &newH0, const int range)
{
if(!newH0.Valid())
return false;
if(CSwingEngine::IsConfirmedSwingHigh(sym, tf, newH0, range))
return true;
for(int sh = 1; sh < newH0.shift; sh++)
{
if(iHigh(sym, tf, sh) >= newH0.price - _Point)
continue;
if(ConfirmedSwingLowAt(sym, tf, sh, range))
return true;
}
return false;
}
static void FinishRoll(HtfContext &ctx)
@@ -71,6 +143,50 @@ class CUpdateEngine
Dbg(StringFormat("Roll xong — bias=%d", ctx.structBias));
}
static void LockBullChochNewL0(HtfContext &ctx)
{
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
ctx.update.newLLocked = true;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
ctx.update.newL02.Clear();
SwingPoint retrace;
if(FindRetraceHighAfterLow(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange, retrace))
{
ctx.update.newH0 = retrace;
Dbg(StringFormat("Bull CHoCH P2 — seed newH0 @ %.5f (hồi sau đáy)", retrace.price));
}
else
ctx.update.newH0.Clear();
Dbg(StringFormat("Bull CHoCH P2 — newL0 khóa @ %.5f sh=%d",
ctx.update.newL0.price, ctx.update.newL0.shift));
}
static void LockBearChochNewH0(HtfContext &ctx)
{
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
ctx.update.newH0Locked = true;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
ctx.update.newL02.Clear();
SwingPoint retrace;
if(FindRetraceLowAfterHigh(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange, retrace))
{
ctx.update.newL0 = retrace;
Dbg(StringFormat("Bear CHoCH P2 — seed newL0 @ %.5f (hồi sau đỉnh)", retrace.price));
}
else
ctx.update.newL0.Clear();
Dbg(StringFormat("Bear CHoCH P2 — newH0 khóa @ %.5f sh=%d",
ctx.update.newH0.price, ctx.update.newH0.shift));
}
public:
static bool IsNewHtfBar(HtfContext &ctx)
{
@@ -78,7 +194,6 @@ public:
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)
@@ -94,7 +209,10 @@ public:
ctx.update.newL0.Clear();
ctx.update.newH0Locked = false;
ctx.update.newLLocked = false;
Dbg("Bull CHoCH — body break L0");
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
ctx.update.case1TrackingH0 = false;
Dbg("Bull CHoCH P1 — body break L0");
}
else if(CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
{
@@ -116,7 +234,10 @@ public:
ctx.update.newH0.Clear();
ctx.update.newH0Locked = false;
ctx.update.newLLocked = false;
Dbg("Bear CHoCH — body break H0");
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
ctx.update.case1TrackingH0 = false;
Dbg("Bear CHoCH P1 — body break H0");
}
else if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.keyLv2.price))
{
@@ -131,7 +252,6 @@ public:
}
}
//--- §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))
@@ -145,12 +265,11 @@ public:
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::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
CSwingEngine::RollBullContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bull Continue — swing high confirmed, rolled");
Dbg("Bull Continue — roll");
}
static void ProcessBearContinueBuild(HtfContext &ctx, const int sh)
@@ -166,144 +285,206 @@ public:
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::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
CSwingEngine::RollBearContinue(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bear Continue — swing low confirmed, rolled");
Dbg("Bear Continue — roll");
}
//--- §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)
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, 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;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
Dbg("Bull CHoCH — newL0 swing confirmed → phase 3");
if(TryConfirmBullChochNewL0(ctx.symbol, ctx.htf, ctx.update.newL0, InpSwingRange))
LockBullChochNewL0(ctx);
}
static void ProcessBearChochBuild(HtfContext &ctx, const int sh)
{
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
if(!ctx.update.newH0Locked)
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, 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;
ctx.update.phase = UPD_PHASE_CHOCH_RESOLVE;
Dbg("Bear CHoCH — newH0 swing confirmed → phase 3");
if(TryConfirmBearChochNewH0(ctx.symbol, ctx.htf, ctx.update.newH0, InpSwingRange))
LockBearChochNewH0(ctx);
}
// P3 case2: newL0 khóa → hồi (newH0) → phá xuống (newL02) → roll bear
static void ProcessBullChochCase2(HtfContext &ctx, const int sh)
{
if(!ctx.update.newLLocked || !ctx.update.newL0.Valid())
return;
if(!CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.update.newL0.price))
{
if(!ctx.update.case2Active)
return;
}
else if(!ctx.update.case2Active)
{
ctx.update.case2Active = true;
ctx.update.newL02.Clear();
ctx.update.case1TrackingH0 = false;
if(ctx.update.newH0.Valid() &&
CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange))
ctx.update.newH0Case2Locked = true;
else
ctx.update.newH0Case2Locked = false;
Dbg("Bull CHoCH P3 case2 — bắt đầu (newL0 đã khóa, phá xuống)");
}
if(!ctx.update.case2Active)
return;
if(!ctx.update.newH0Case2Locked)
{
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, false);
if(CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newH0, InpSwingRange))
{
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
ctx.update.newH0Case2Locked = true;
ctx.update.newL02.Clear();
Dbg(StringFormat("Bull CHoCH case2 — newH0 confirm @ %.5f", ctx.update.newH0.price));
}
return;
}
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
if(!CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL02, InpSwingRange))
return;
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL02.shift, ctx.update.newL02);
CSwingEngine::RollBullChochCase2(ctx.swings,
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
FinishRoll(ctx);
Dbg("Bull CHoCH case2 — roll bear (H0→H1, newH0→H0, newL0→L1, newL02→L0)");
}
//--- §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))
{
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
if(!ctx.update.case1TrackingH0)
{
ctx.update.case1TrackingH0 = true;
ctx.update.newH0.Clear();
ctx.update.newH0Locked = false;
Dbg("Bull CHoCH case1 — break H0, track newH0");
Dbg("Bull CHoCH case1 — phá H0, track newH0");
}
if(CKeyLevels::IsBullCandle(ctx.symbol, ctx.htf, sh))
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newH0, ctx.update.newH0Locked);
if(ctx.update.case1TrackingH0 && !ctx.update.newH0Locked &&
if(!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::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newH0.shift, ctx.update.newH0);
CSwingEngine::RollBullChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bull CHoCH case1 — newH0 confirmed, rolled");
Dbg("Bull CHoCH case1 — roll bull");
}
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);
ProcessBullChochCase2(ctx, sh);
}
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 ProcessBearChochCase2(HtfContext &ctx, const int sh)
{
if(!ctx.update.newH0Locked || !ctx.update.newH0.Valid())
return;
if(!CKeyLevels::BodyBreakAbove(ctx.symbol, ctx.htf, sh, ctx.update.newH0.price))
{
if(!ctx.update.case2Active)
return;
}
else if(!ctx.update.case2Active)
{
ctx.update.case2Active = true;
ctx.update.newL02.Clear();
ctx.update.case1TrackingH0 = false;
if(ctx.update.newL0.Valid() &&
CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange))
ctx.update.newH0Case2Locked = true;
else
ctx.update.newH0Case2Locked = false;
Dbg("Bear CHoCH P3 case2 — bắt đầu");
}
if(!ctx.update.case2Active)
return;
if(!ctx.update.newH0Case2Locked)
{
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, false);
if(CSwingEngine::IsConfirmedSwingLow(ctx.symbol, ctx.htf,
ctx.update.newL0, InpSwingRange))
{
CSwingEngine::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
ctx.update.newH0Case2Locked = true;
ctx.update.newL02.Clear();
Dbg(StringFormat("Bear CHoCH case2 — newL0 retrace confirm @ %.5f", ctx.update.newL0.price));
}
return;
}
TrackNewHigh(ctx.symbol, ctx.htf, sh, ctx.update.newL02, false);
if(!CSwingEngine::IsConfirmedSwingHigh(ctx.symbol, ctx.htf,
ctx.update.newL02, InpSwingRange))
return;
CSwingEngine::FillSwingHigh(ctx.symbol, ctx.htf, ctx.update.newL02.shift, ctx.update.newL02);
CSwingEngine::RollBearChochCase2(ctx.swings,
ctx.update.newH0, ctx.update.newL0, ctx.update.newL02);
FinishRoll(ctx);
Dbg("Bear CHoCH case2 — roll bull");
}
static void ProcessBearChochResolve(HtfContext &ctx, const int sh)
{
if(CKeyLevels::BodyBreakBelow(ctx.symbol, ctx.htf, sh, ctx.swings.l0.price))
{
ctx.update.case2Active = false;
ctx.update.newH0Case2Locked = false;
if(!ctx.update.case1TrackingH0)
{
ctx.update.case1TrackingH0 = true;
ctx.update.newL0.Clear();
ctx.update.newLLocked = false;
Dbg("Bear CHoCH case1 — break L0, track newL0");
Dbg("Bear CHoCH case1 — phá L0");
}
if(CKeyLevels::IsBearCandle(ctx.symbol, ctx.htf, sh))
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
TrackNewLow(ctx.symbol, ctx.htf, sh, ctx.update.newL0, ctx.update.newLLocked);
if(ctx.update.case1TrackingH0 && !ctx.update.newLLocked &&
if(!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::FillSwingLow(ctx.symbol, ctx.htf, ctx.update.newL0.shift, ctx.update.newL0);
CSwingEngine::RollBearChochCase1(ctx.swings, ctx.update.newH0, ctx.update.newL0);
FinishRoll(ctx);
Dbg("Bear CHoCH case1 — newL0 confirmed, rolled");
Dbg("Bear CHoCH case1 — roll bear");
}
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");
}
}
ProcessBearChochCase2(ctx, sh);
}
static void OnHtfBarClose(HtfContext &ctx)
+79 -2
View File
@@ -61,6 +61,17 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
const bool phaseFilterEnabled,
const bool trendFilterEnabled,
const bool atrFilterEnabled,
const bool adxFilterEnabled,
const bool adxOkAtBar,
const double adxVal,
const double plusDi,
const double minusDi,
const string adxFailWhy,
const bool swingFilterEnabled,
const bool swingOkAtBar,
const double swingBodyOld,
const double swingBodyNew,
const string swingFailWhy,
const bool rsiObOsFilterEnabled,
const double rsiOverbought,
const double rsiOversold,
@@ -149,6 +160,30 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
if(atrFilterEnabled && !atrExpAtBar)
SignalEval_SetFail(r.failTag, "ATR co");
if(!adxFilterEnabled)
SignalEval_Append(r.detail, "ADX:OFF");
else if(adxOkAtBar)
SignalEval_Append(r.detail, StringFormat("ADX:OK(%.1f +DI=%.1f -DI=%.1f)", adxVal, plusDi, minusDi));
else
{
SignalEval_Append(r.detail, StringLen(adxFailWhy) > 0
? StringFormat("ADX:FAIL(%.1f %s)", adxVal, adxFailWhy)
: StringFormat("ADX:FAIL(%.1f)", adxVal));
SignalEval_SetFail(r.failTag, "ADX yếu");
}
if(!swingFilterEnabled)
SignalEval_Append(r.detail, "Swing2:OFF");
else if(swingOkAtBar)
SignalEval_Append(r.detail, StringFormat("Swing2:OK(đáy %.5f→%.5f)", swingBodyOld, swingBodyNew));
else
{
SignalEval_Append(r.detail, StringLen(swingFailWhy) > 0
? "Swing2:FAIL " + swingFailWhy
: "Swing2:FAIL");
SignalEval_SetFail(r.failTag, "2 đáy");
}
bool rsiOk = true;
if(!rsiObOsFilterEnabled)
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
@@ -178,7 +213,10 @@ SignalEvalResult Signal_EvaluateBuyAt(const int shift, const int rates_total, co
}
r.signalOk = cross && coreOk && trendUp
&& (!atrFilterEnabled || atrExpAtBar) && rsiOk && envBar;
&& (!atrFilterEnabled || atrExpAtBar)
&& (!adxFilterEnabled || adxOkAtBar)
&& (!swingFilterEnabled || swingOkAtBar)
&& rsiOk && envBar;
r.tradeOk = r.signalOk;
if(r.signalOk && r.tradeOk)
@@ -200,6 +238,17 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
const bool phaseFilterEnabled,
const bool trendFilterEnabled,
const bool atrFilterEnabled,
const bool adxFilterEnabled,
const bool adxOkAtBar,
const double adxVal,
const double plusDi,
const double minusDi,
const string adxFailWhy,
const bool swingFilterEnabled,
const bool swingOkAtBar,
const double swingBodyOld,
const double swingBodyNew,
const string swingFailWhy,
const bool rsiObOsFilterEnabled,
const double rsiOverbought,
const double rsiOversold,
@@ -287,6 +336,30 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
if(atrFilterEnabled && !atrExpAtBar)
SignalEval_SetFail(r.failTag, "ATR co");
if(!adxFilterEnabled)
SignalEval_Append(r.detail, "ADX:OFF");
else if(adxOkAtBar)
SignalEval_Append(r.detail, StringFormat("ADX:OK(%.1f +DI=%.1f -DI=%.1f)", adxVal, plusDi, minusDi));
else
{
SignalEval_Append(r.detail, StringLen(adxFailWhy) > 0
? StringFormat("ADX:FAIL(%.1f %s)", adxVal, adxFailWhy)
: StringFormat("ADX:FAIL(%.1f)", adxVal));
SignalEval_SetFail(r.failTag, "ADX yếu");
}
if(!swingFilterEnabled)
SignalEval_Append(r.detail, "Swing2:OFF");
else if(swingOkAtBar)
SignalEval_Append(r.detail, StringFormat("Swing2:OK(đỉnh %.5f→%.5f)", swingBodyOld, swingBodyNew));
else
{
SignalEval_Append(r.detail, StringLen(swingFailWhy) > 0
? "Swing2:FAIL " + swingFailWhy
: "Swing2:FAIL");
SignalEval_SetFail(r.failTag, "2 đỉnh");
}
bool rsiOk = true;
if(!rsiObOsFilterEnabled)
SignalEval_Append(r.detail, "RSI OB/OS:OFF");
@@ -315,7 +388,11 @@ SignalEvalResult Signal_EvaluateSellAt(const int shift, const int rates_total, c
SignalEval_SetFail(r.failTag, "Phiên");
}
r.signalOk = cross && coreOk && trendDown && (!atrFilterEnabled || atrExpAtBar) && rsiOk && envBar;
r.signalOk = cross && coreOk && trendDown
&& (!atrFilterEnabled || atrExpAtBar)
&& (!adxFilterEnabled || adxOkAtBar)
&& (!swingFilterEnabled || swingOkAtBar)
&& rsiOk && envBar;
r.tradeOk = r.signalOk;
if(r.signalOk && r.tradeOk)