572 lines
18 KiB
MQL5
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
| |
| |
*/
| |
//+------------------------------------------------------------------+
|