mql5/Experts/Motivated/Systems/StructureSystem.mqh
younglubah-gmail 9639b8b362
2026-06-23 21:44:37 +02:00

572 lines
18 KiB
MQL5

//+------------------------------------------------------------------+
//| StructureSystem.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "EventBus.mqh"
#include "AbstractSystem.mqh"
#include "../Data/StructureState.mqh"
#include "../Interfaces/Events/IStructEvent.mqh"
#include <ABCD/chartObjects.mqh>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class StructureSystem : public AbstractSystem
{
private:
StructureState *m_state;
int m_swingStrength;
bool m_trendBullish;
//--- startup / seeking flags
bool bullNoBosActive, bullSeekingReplacementNoBos;
bool bearNoBosActive, bearSeekingReplacementNoBos;
bool isStartup;
//--- NO BOS (resistance / support)
// TimePrice levels.bullNoBos;
// TimePrice levels.bearNoBos;
//--- EXTREMES (lowest / highest after NoBos)
TimePrice noBullBosLow;
TimePrice noBearBosHigh;
//--- BOS (Break of Structure)
TimePrice bosBull1;//, levels.bullConfirmedLow;
TimePrice bosBull1B, confirmedLowB;
TimePrice bosBull2;//, levels.protectedLow;
TimePrice bosBull2B, protectedLowB;
TimePrice bosBear1;//, levels.bearConfirmedHigh;
TimePrice bosBear1B, confirmedHighB;
TimePrice bosBear2;//, levels.protectedHigh;
TimePrice bosBear2B, protectedHighB;
// int levels.direction; // -1 bearish, 0 neutral/startup, 1 bullish
//--- private methods
void UpdateSwingPoints(int i);
void OnInitTracking(bool isBull, int i);
void UpdateTracker(bool isBull, int i);
void FindDirection(int i);
bool CheckBreak(bool isAbove, int i, TimePrice &tp)
{
if(isAbove)
{
if(com.CloseAt(i) > tp.y && com.IsSwingHigh(i))
return true;
return false;
}
//--- BearBreak
if(com.CloseAt(i) < tp.y && com.IsSwingLow(i))
return true;
return false;
}
void UpdateNoBosLive(int i)
{
datetime t = com.TimeAt(i);
if(levels.direction == 1)
{
if(com.LowAt(i) < noBullBosLow.y)
noBullBosLow.Set(t, com.LowAt(i));
return;
}
if(levels.direction == -1)
{
if(com.HighAt(i) > noBearBosHigh.y)
noBearBosHigh.Set(t, com.HighAt(i));
}
}
//-----------------------------------------------------------------
// CHoCH = Change of Character – now only sets the B‑point, NO reversal
//-----------------------------------------------------------------
void CheckChoch(int i)
{
datetime t = com.TimeAt(i);
if(levels.direction == 1)
{
if(CheckBreak(false, i, levels.bullConfirmedLow) && !confirmedLowB.Ok())
confirmedLowB.Set(t, com.LowAt(i));
}
if(levels.direction == -1)
{
if(CheckBreak(true, i, levels.bearConfirmedHigh) && !confirmedHighB.Ok())
confirmedHighB.Set(t, com.HighAt(i));
}
}
void CheckBos(int i)
{
datetime t = com.TimeAt(i);
//--- Bullish BOS (break above levels.bullNoBos)
if(levels.direction == 1)
{
if(CheckBreak(true, i, levels.bullNoBos))
{
if(noBullBosLow.y > levels.bullConfirmedLow.y)
{
levels.protectedLow = levels.bullConfirmedLow;
protectedLowB.Reset();
bosBull2 = bosBull1;
bosBull2B = bosBull1B;
}
levels.bullConfirmedLow = noBullBosLow;
confirmedLowB.Reset();
bosBull1 = levels.bullNoBos;
bosBull1B.Set(t, levels.bullNoBos.y);
levels.bullNoBos.Set(t, com.HighAt(i));
noBullBosLow.Set(t, com.LowAt(i));
levels.direction = 1;
return;
}
}
//--- Bearish BOS (break below levels.bearNoBos)
if(levels.direction == -1)
{
if(CheckBreak(false, i, levels.bearNoBos))
{
if(noBearBosHigh.y < levels.bearConfirmedHigh.y)
{
levels.protectedHigh = levels.bearConfirmedHigh;
protectedHighB.Reset();
bosBear2 = bosBear1;
bosBear2B = bosBear1B;
}
levels.bearConfirmedHigh = noBearBosHigh;
confirmedHighB.Reset();
bosBear1 = levels.bearNoBos;
bosBear1B.Set(t, levels.bearNoBos.y);
levels.bearNoBos.Set(t, com.LowAt(i));
noBearBosHigh.Set(t, com.HighAt(i));
levels.direction = -1;
return;
}
}
}
void CheckReversal(int i)
{
datetime t = com.TimeAt(i);
//--- Bullish → Bearish reversal
if(levels.direction == 1)
{
// Case 1: Protected low exists → use it as reversal level
if(levels.protectedLow.Ok())
{
if(CheckBreak(false, i, levels.protectedLow))
{
// Flip to bearish
levels.bearConfirmedHigh = levels.bullNoBos;
confirmedHighB.Reset();
bosBear1 = levels.bearNoBos;
bosBear1B.Set(t, levels.bearNoBos.y);
levels.bearNoBos.Set(t, com.LowAt(i));
noBearBosHigh.Set(t, com.HighAt(i));
levels.bullNoBos.Reset();
noBullBosLow.Reset();
levels.bullConfirmedLow.Reset();
levels.protectedLow.Reset();
protectedLowB.Reset();
levels.direction = -1;
}
return;
}
// Case 2: No protected low → use confirmed low as fallback
else
{
if(CheckBreak(false, i, levels.bullConfirmedLow))
{
levels.bearConfirmedHigh = levels.bullNoBos;
confirmedHighB.Reset();
bosBear1 = levels.bearNoBos;
bosBear1B.Set(t, levels.bearNoBos.y);
levels.bearNoBos.Set(t, com.LowAt(i));
noBearBosHigh.Set(t, com.HighAt(i));
levels.bullNoBos.Reset();
noBullBosLow.Reset();
levels.bullConfirmedLow.Reset();
levels.protectedLow.Reset();
protectedLowB.Reset();
levels.direction = -1;
}
return;
}
}
//--- Bearish → Bullish reversal (symmetrical to bullish side)
if(levels.direction == -1)
{
// Case 1: Protected high exists → use it as reversal level
if(levels.protectedHigh.Ok())
{
if(CheckBreak(true, i, levels.protectedHigh))
{
// Flip to bullish
levels.bullConfirmedLow = levels.bearNoBos;
confirmedLowB.Reset();
bosBull1 = levels.bullNoBos;
bosBull1B.Set(t, levels.bullNoBos.y);
levels.bullNoBos.Set(t, com.HighAt(i));
noBullBosLow.Set(t, com.LowAt(i));
levels.bearNoBos.Reset();
noBearBosHigh.Reset();
levels.bearConfirmedHigh.Reset();
levels.protectedHigh.Reset();
protectedHighB.Reset();
levels.direction = 1;
}
return;
}
// Case 2: No protected high → use confirmed high as fallback
else
{
if(CheckBreak(true, i, levels.bearConfirmedHigh))
{
levels.bullConfirmedLow = levels.bearNoBos;
confirmedLowB.Reset();
bosBull1 = levels.bullNoBos;
bosBull1B.Set(t, levels.bullNoBos.y);
levels.bullNoBos.Set(t, com.HighAt(i));
noBullBosLow.Set(t, com.LowAt(i));
levels.bearNoBos.Reset();
noBearBosHigh.Reset();
levels.bearConfirmedHigh.Reset();
levels.protectedHigh.Reset();
protectedHighB.Reset();
levels.direction = 1;
}
return;
}
}
}
public:
StructureSystem();
~StructureSystem() { delete m_state; };
virtual int OnInit(StructureLevels* levels_, EventBus &_eventBus) override
{
AbstractSystem::OnInit(levels_, &_eventBus);
m_state = new StructureState();
isStartup = true;
bullNoBosActive = false;
bearNoBosActive = false;
bullSeekingReplacementNoBos = true;
bearSeekingReplacementNoBos = true;
levels.bullNoBos.Reset();
noBullBosLow.Reset();
levels.bullConfirmedLow.Reset();
levels.protectedLow.Reset();
protectedLowB.Reset();
levels.bearNoBos.Reset();
noBearBosHigh.Reset();
levels.bearConfirmedHigh.Reset();
levels.protectedHigh.Reset();
protectedHighB.Reset();
levels.direction = 0;
return INIT_SUCCEEDED;
}
virtual void OnCalculate(int i, CommonHelper &commonHelper) override;
bool IsBullish() { return m_trendBullish; }
StructureState *GetState() { return m_state; }
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
StructureSystem::StructureSystem()
{
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void StructureSystem::OnCalculate(int i, CommonHelper &commonHelper)
{
AbstractSystem::OnCalculate(i, commonHelper);
if(!com.IsValidIndex(i))
return;
datetime curBarTime = com.TimeAt(i);
m_state.ApplyDecay(curBarTime);
UpdateSwingPoints(i);
//--- Startup: locate first NoBos
if(!bullNoBosActive && bullSeekingReplacementNoBos && isStartup)
{
OnInitTracking(true, i);
return;
}
if(!bearNoBosActive && bearSeekingReplacementNoBos && isStartup)
{
OnInitTracking(false, i);
}
if(isStartup && bearNoBosActive && bullNoBosActive)
{
// Align extremes
if(levels.bullNoBos.x < levels.bearNoBos.x)
noBullBosLow.Set(levels.bearNoBos);
else
noBearBosHigh.Set(levels.bullNoBos);
isStartup = false;
}
else
if(!isStartup && (!bearNoBosActive || !bullNoBosActive))
return;
FindDirection(i);
UpdateNoBosLive(i);
CheckBos(i);
CheckChoch(i);
CheckReversal(i);
// Only updates B‑points, no reversal
// Actual reversal logic
//--- Visual objects
ObjectsDeleteAll(0, 0, OBJ_TREND);
Comment(levels.direction);
if(levels.direction == 1) // Bullish
{
if(levels.bullNoBos.Ok())
createTrend("nobos", levels.bullNoBos.x, levels.bullNoBos.y, curBarTime, levels.bullNoBos.y, clrBlue);
if(noBullBosLow.Ok())
createTrend("noBullBosLow", noBullBosLow.x, noBullBosLow.y, curBarTime, noBullBosLow.y, clrBlue);
if(bosBull1.Ok())
createTrend("bosBull1", bosBull1.x, bosBull1.y, bosBull1B.x, bosBull1B.y, clrBlue);
if(levels.bullConfirmedLow.Ok())
createTrend("levels.bullConfirmedLow", levels.bullConfirmedLow.x, levels.bullConfirmedLow.y, confirmedLowB.x == 0 ? curBarTime : confirmedLowB.x, levels.bullConfirmedLow.y, clrBlue);
if(bosBull2.Ok())
createTrend("bosBull2", bosBull2.x, bosBull2.y, bosBull2B.x, bosBull2B.y, clrBlue);
if(levels.protectedLow.Ok())
createTrend("levels.protectedLow", levels.protectedLow.x, levels.protectedLow.y, protectedLowB.x == 0 ? curBarTime : levels.protectedLow.x, levels.protectedLow.y, clrBlue);
}
else
if(levels.direction == -1) // Bearish
{
if(noBearBosHigh.Ok())
createTrend("noBearBosHigh", noBearBosHigh.x, noBearBosHigh.y, curBarTime, noBearBosHigh.y, clrBrown);
if(levels.bearNoBos.Ok())
createTrend("levels.bearNoBos", levels.bearNoBos.x, levels.bearNoBos.y, curBarTime, levels.bearNoBos.y, clrRed);
if(bosBear1.Ok())
createTrend("bosBear1", bosBear1.x, bosBear1.y, bosBear1B.x, bosBear1B.y, clrAquamarine);
if(levels.bearConfirmedHigh.Ok())
createTrend("levels.bearConfirmedHigh", levels.bearConfirmedHigh.x, levels.bearConfirmedHigh.y, confirmedHighB.x == 0 ? curBarTime : confirmedHighB.x, levels.bearConfirmedHigh.y, clrBlue);
if(bosBear2.Ok())
createTrend("bosBear2", bosBear2.x, bosBear2.y, bosBear2B.x, bosBear2B.y, clrAquamarine);
if(levels.protectedHigh.Ok())
createTrend("levels.protectedHigh", levels.protectedHigh.x, levels.protectedHigh.y, protectedHighB.x == 0 ? curBarTime : levels.protectedHigh.x, levels.protectedHigh.y, clrBlue);
}
else // levels.direction == 0 (startup)
{
if(levels.bullNoBos.Ok())
createTrend("nobos", levels.bullNoBos.x, levels.bullNoBos.y, levels.bullNoBos.x + PeriodSeconds(PERIOD_D1), levels.bullNoBos.y, clrBlue);
if(levels.bearNoBos.Ok())
createTrend("levels.bearNoBos", levels.bearNoBos.x, levels.bearNoBos.y, levels.bearNoBos.x + PeriodSeconds(PERIOD_D1), levels.bearNoBos.y, clrRed);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void StructureSystem::FindDirection(int i)
{
if(levels.direction == 0)
{
// Bullish breakout
if(CheckBreak(true, i, levels.bullNoBos))
{
bosBull1.Set(levels.bullNoBos);
bosBull1B.Set(com.TimeAt(i), levels.bullNoBos.y);
levels.bullConfirmedLow.Set(noBullBosLow);
levels.bullNoBos.Set(com.TimeAt(i), com.SwingHigh(i));
levels.direction = 1;
return;
}
// Bearish breakout
if(CheckBreak(false, i, levels.bearNoBos))
{
bosBear1.Set(levels.bearNoBos);
bosBear1B.Set(com.TimeAt(i), levels.bearNoBos.y);
levels.bearConfirmedHigh.Set(noBearBosHigh);
levels.bearNoBos.Set(com.TimeAt(i), com.SwingLow(i));
levels.direction = -1;
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void StructureSystem::OnInitTracking(bool isBull, int i)
{
if(isBull)
{
if(!com.IsSwingHigh(i))
return;
levels.bullNoBos.Set(i, com.TimeAt(i), com.HighAt(i));
noBullBosLow.Set(i, com.TimeAt(i), com.LowAt(i));
bullNoBosActive = true;
return;
}
if(!com.IsSwingLow(i))
return;
levels.bearNoBos.Set(i, com.TimeAt(i), com.LowAt(i));
noBearBosHigh.Set(i, com.TimeAt(i), com.HighAt(i));
bearNoBosActive = true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void StructureSystem::UpdateSwingPoints(int i)
{
if(com.IsSwingHigh(i))
{
double level = com.HighAt(i);
datetime t = com.TimeAt(i);
m_state.AddSwing(SWING_HIGH, level, t, com.TimeframeMinutes());
IEvent *e = new CStructEvent(STRUCT_DETECT_SWING_HIGH, level, t, t, com.TimeframeMinutes());
eventBus.Publish(e);
}
if(com.IsSwingLow(i))
{
double level = com.LowAt(i);
datetime t = com.TimeAt(i);
m_state.AddSwing(SWING_LOW, level, t, com.TimeframeMinutes());
IEvent *e = new CStructEvent(STRUCT_DETECT_SWING_LOW, level, t, t, com.TimeframeMinutes());
eventBus.Publish(e);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void StructureSystem::UpdateTracker(bool isBull, int i)
{
// Placeholder – no additional tracking logic required yet
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//use same tracking system add internals..also store(ithase 3 timeprices..start,exteme,end)and draw
/*
Round 1 — Musical Alphabet
1. A,A#,B,C,C#,D,D#,E,F,F#,G,G#
2. B&C, E&F
3. B&C, E&F
4. G#/Ab
5. C#/Db
Round 2 — Chromatic Scale
- C,C#,D,D#,E,F,F#,G,G#,A,A#,B,C
- A,A#,B,C,C#,D,D#,E,F,F#,G,G#,A
Round 3 — Intervals
1. 2
2. 3
3. 4
4. 5
5. 6
6. 7
7. Eb
8. E
9. Gb
10. 5
11. Bb
12. B
Round 4 — Major Scale Formula
WS,WS,HS,WS,WS,WS,HS
Round 5 — Build Scales
1. C,D,E,F,G,A,B,C
2. G,A,B,C,D,E,F#,G
3. D,E,F#,G,A,B,C#,D
4. F,G,A,A#,C,D,E,F
5. A,B,C#,D,E,F#,G#,A
Round 6 — Fretboard Knowledge
1. 4th
2. 4th
3. 4th
4. 4th
5. 3rd
6. 4th
Round 7 — Instant Recognition
1. major triad
2. minor triad
3. diminished triad
4. augmented triads
Round 8 — Killer Test
A,A#,B,C,C#,D,D#,E,F,F#,G,G#
A,Bb,B,C,Db,D,Eb,E,F,Gb,G,Ab
1. Bb,C,D,Eb,F,G,A,Bb
2. Eb,F,G,Ab,Bb,C,D,Eb
3. E,Gb,G#,A,B,Db,D#,E
4. B,Db,Eb,E,Gb,Ab,A#,B
5. Ab,Bb,C,Db,Eb,F,G,Ab
*/
//+------------------------------------------------------------------+