Update all
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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 P1–2: 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user