mql5/Experts/AnotherTriBible/Chapter2.mqh
younglubah-gmail 540d58914e
2026-03-15 02:46:38 +02:00

424 lines
12 KiB
MQL5

//+------------------------------------------------------------------+
//| C_Chapter2.mqh|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "AbstractChapter.mqh"
input int ba = 3;
class C_Chapter2 : public CAbstractChapter {
private:
//========================
// Bull state
//========================
int bosHigh, bosHigh0, noBosHigh;
int bosHighbreakIndex, bosHighbreakIndex0;
string protectedLowText;
//========================
// Bear state
//========================
int bosLow, bosLow0, noBosLow;
int bosLowbreakIndex, bosLowbreakIndex0;
string protectedHighText;
//========================
// Gates (independent engines)
//========================
int bullStartFrom;
int bearStartFrom;
int protectedHighIndex;
int protectedLowIndex;
//========================
// Keys
//========================
string K_B_NoBosHigh() {
return "CH2_B_NoBosHigh";
}
string K_B_BosHigh() {
return "CH2_B_BosHigh";
}
string K_B_BosHigh0() {
return "CH2_B_BosHigh0";
}
string K_B_ConfLow() {
return "CH2_B_ConfLow";
}
string K_B_ProtLow() {
return "CH2_B_ProtLow";
}
string K_S_NoBosLow() {
return "CH2_S_NoBosLow";
}
string K_S_BosLow() {
return "CH2_S_BosLow";
}
string K_S_BosLow0() {
return "CH2_S_BosLow0";
}
string K_S_ConfHigh() {
return "CH2_S_ConfHigh";
}
string K_S_ProtHigh() {
return "CH2_S_ProtHigh";
}
//========================
// Draw
//========================
void DrawLine(string key, int fromIndex, int toIndex, double price, string text, color col = clrChartreuse, int style = 1) {
if(fromIndex < 0 || toIndex < 0) return;
if(fromIndex >= bar.Size() || toIndex >= bar.Size()) return;
debug.LineWithCenteredText(
key,
bar.Time(fromIndex), price,
bar.Time(toIndex), price,
text,
style, col
);
}
//========================
// Breaks
//========================
bool BreakAbove(int refIndex, int barIndex) {
return bar.Close(barIndex) > bar.High(refIndex);
}
bool BreakBelow(int refIndex, int barIndex) {
return bar.Close(barIndex) < bar.Low(refIndex);
}
//========================
// Extremes between points
//========================
int findLowest(int fromIndex, int toIndex) {
int lowestIndex = -1;
for(int i = fromIndex + 1; i < toIndex; i++) {
if(lowestIndex == -1 || bar.Low(i) < bar.Low(lowestIndex))
lowestIndex = i;
}
return lowestIndex;
}
int findHighest(int fromIndex, int toIndex) {
int highestIndex = -1;
for(int i = fromIndex + 1; i < toIndex; i++) {
if(highestIndex == -1 || bar.High(i) > bar.High(highestIndex))
highestIndex = i;
}
return highestIndex;
}
//========================
// Find Nth previous VALID swing from an anchor (inclusive)
// n=0 -> current if valid, n=1 -> 1 swing before, n=2 -> 2 swings before
//========================
int PrevSwingHighIndex(int anchor, int n) {
int count = -1;
for(int i = anchor; i >= 0; i--) {
if(swing.IsValidValue(swing.HighAt(i))) {
count++;
if(count == n) return i;
}
}
return -1;
}
int PrevSwingLowIndex(int anchor, int n) {
int count = -1;
for(int i = anchor; i >= 0; i--) {
if(swing.IsValidValue(swing.LowAt(i))) {
count++;
if(count == n) return i;
}
}
return -1;
}
//========================
// Engines (pure updates, independent)
//========================
void UpdateBullFrom(int start) {
for(int i = start; i < bar.Size() - 1; i++) {
double up = swing.HighAt(i);
if(swing.IsValidValue(up)) {
if(bosHigh >= 0) {
if(up > bar.High(bosHigh) && (noBosHigh < 0 || up > bar.High(noBosHigh)))
noBosHigh = i;
} else {
if(noBosHigh < 0 || up > bar.High(noBosHigh))
noBosHigh = i;
}
}
if(noBosHigh != -1) {
if(BreakAbove(noBosHigh, i) && swing.IsValidValue(swing.HighAt(noBosHigh))) {
bosHigh0 = bosHigh;
bosHighbreakIndex0 = bosHighbreakIndex;
bosHigh = noBosHigh;
bosHighbreakIndex = i;
noBosHigh = i;
}
}
if(noBosHigh >= 0 && !swing.IsValidValue(swing.HighAt(noBosHigh))) {
if(bar.High(i) > bar.High(noBosHigh))
noBosHigh = i;
}
}
}
void UpdateBearFrom(int start) {
for(int i = start; i < bar.Size() - 1; i++) {
double dn = swing.LowAt(i);
if(swing.IsValidValue(dn)) {
if(bosLow >= 0) {
if(dn < bar.Low(bosLow) && (noBosLow < 0 || dn < bar.Low(noBosLow)))
noBosLow = i;
} else {
if(noBosLow < 0 || dn < bar.Low(noBosLow))
noBosLow = i;
}
}
if(noBosLow != -1) {
if(BreakBelow(noBosLow, i) && swing.IsValidValue(swing.LowAt(noBosLow))) {
bosLow0 = bosLow;
bosLowbreakIndex0 = bosLowbreakIndex;
bosLow = noBosLow;
bosLowbreakIndex = i;
noBosLow = i;
}
}
if(noBosLow >= 0 && !swing.IsValidValue(swing.LowAt(noBosLow))) {
if(bar.Low(i) < bar.Low(noBosLow))
noBosLow = i;
}
}
}
//========================
// Pipelines (draw + detect reversal)
//========================
void confirmedLowChoch(int pNoBosHigh, int pBosHighBreak) {
DrawLine(K_B_BosHigh(), pNoBosHigh, pBosHighBreak, bar.High(pNoBosHigh), "BOS", clrChartreuse, 1);
int confirmedLow = findLowest(pNoBosHigh, pBosHighBreak);
int breakBar = bar.Size() - 1;
string text = "Confirmed Low";
for(int i = confirmedLow; i < bar.Size() - 1; i++) {
if(BreakBelow(confirmedLow, i)) {
text = "CHOCH";
protectedLowText = "Protected Low";
breakBar = i;
break;
} else {
protectedLowText = "Confirmed Low";
}
}
DrawLine(K_B_ConfLow(), confirmedLow, breakBar, bar.Low(confirmedLow), text, clrChartreuse, 1);
}
// returns: START INDEX = 2 swings before the break (low side)
int protectedLowBos(int pBosHigh, int pBosHighBreak) {
DrawLine(K_B_BosHigh0(), pBosHigh, pBosHighBreak, bar.High(pBosHigh), "BOS", clrChartreuse, 1);
int protectedLow = findLowest(pBosHigh, pBosHighBreak);
int breakBar = bar.Size() - 1;
string text = protectedLowText;
for(int i = protectedLow; i < bar.Size() - 1; i++) {
if(BreakBelow(protectedLow, i)) {
text = "BOS REVERSAL";
breakBar = i;
break;
}
}
protectedLowIndex=protectedLow;
DrawLine(K_B_ProtLow(), protectedLow, breakBar, bar.Low(protectedLow), text, clrChartreuse, 1);
if(text == "BOS REVERSAL") {
// anchor at breakBar, take 2 swing lows before break
return PrevSwingLowIndex(breakBar, ba);
}
return -1;
}
void confirmedHighChoch(int pNoBosLow, int pBosLowBreak) {
DrawLine(K_S_BosLow(), pNoBosLow, pBosLowBreak, bar.Low(pNoBosLow), "BOS", clrTomato, 1);
int confirmedHigh = findHighest(pNoBosLow, pBosLowBreak);
int breakBar = bar.Size() - 1;
string text = "Confirmed High";
for(int i = confirmedHigh; i < bar.Size() - 1; i++) {
if(BreakAbove(confirmedHigh, i)) {
text = "CHOCH";
protectedHighText = "Protected High";
breakBar = i;
break;
} else {
protectedHighText = "Confirmed High";
}
}
DrawLine(K_S_ConfHigh(), confirmedHigh, breakBar, bar.High(confirmedHigh), text, clrTomato, 1);
}
// returns: START INDEX = 2 swings before the break (high side)
int protectedHighBos(int pBosLow, int pBosLowBreak) {
DrawLine(K_S_BosLow0(), pBosLow, pBosLowBreak, bar.Low(pBosLow), "BOS", clrTomato, 1);
int protectedHigh = findHighest(pBosLow, pBosLowBreak);
int breakBar = bar.Size() - 1;
string text = protectedHighText;
for(int i = protectedHigh; i < bar.Size() - 1; i++) {
if(BreakAbove(protectedHigh, i)) {
text = "BOS REVERSAL";
breakBar = i;
break;
}
}
protectedHighIndex = protectedHigh;
DrawLine(K_S_ProtHigh(), protectedHigh, breakBar, bar.High(protectedHigh), text, clrTomato, 1);
if(text == "BOS REVERSAL") {
// anchor at breakBar, take 2 swing highs before break
return PrevSwingHighIndex(breakBar, ba);
}
return -1;
}
//========================
// Relationship switches (start from 2 swings before break)
//========================
void SwitchToBearFromBull(int startSwingLow) {
bearStartFrom = (startSwingLow >= 0 ? startSwingLow : bearStartFrom);
bosLow = -1;
bosLow0 = -1;
bosLowbreakIndex = -1;
bosLowbreakIndex0 = -1;
protectedHighText = "";
noBosLow = startSwingLow;
}
void SwitchToBullFromBear(int startSwingHigh) {
bullStartFrom = (startSwingHigh >= 0 ? startSwingHigh : bullStartFrom);
bosHigh = -1;
bosHigh0 = -1;
bosHighbreakIndex = -1;
bosHighbreakIndex0 = -1;
protectedLowText = "";
noBosHigh = startSwingHigh;
}
public:
C_Chapter2();
~C_Chapter2();
void OnCalculate();
// >>> getters for Chapter 3 wiring
int GetProtectedHighIndex() { return protectedHighIndex; }
int GetProtectedLowIndex() { return protectedLowIndex; }
};
//+------------------------------------------------------------------+
C_Chapter2::C_Chapter2() {
bosHigh = -1;
bosHigh0 = -1;
noBosHigh = -1;
bosHighbreakIndex = -1;
bosHighbreakIndex0 = -1;
protectedLowText = "";
bosLow = -1;
bosLow0 = -1;
noBosLow = -1;
bosLowbreakIndex = -1;
bosLowbreakIndex0 = -1;
protectedHighText = "";
bullStartFrom = 0;
bearStartFrom = 0;
}
C_Chapter2::~C_Chapter2() {}
//+------------------------------------------------------------------+
void C_Chapter2::OnCalculate() {
if(OnNewBar())
return;
bar.UpdateFromMonthsBack(symbol, timeframe, 3);
// Update both engines every time (independent)
UpdateBullFrom(bullStartFrom);
UpdateBearFrom(bearStartFrom);
// Draw CHOCH legs
if(bosHigh >= 0 && bosHighbreakIndex >= 0)
confirmedLowChoch(bosHigh, bosHighbreakIndex);
if(bosLow >= 0 && bosLowbreakIndex >= 0)
confirmedHighChoch(bosLow, bosLowbreakIndex);
// Detect reversals: start opposite engine from 2 swings before break
int startBearFromSwingLow = -1;
int startBullFromSwingHigh = -1;
if(bosHigh0 >= 0 && bosHighbreakIndex0 >= 0)
startBearFromSwingLow = protectedLowBos(bosHigh0, bosHighbreakIndex0);
if(bosLow0 >= 0 && bosLowbreakIndex0 >= 0)
startBullFromSwingHigh = protectedHighBos(bosLow0, bosLowbreakIndex0);
if(startBearFromSwingLow >= 0) {
SwitchToBearFromBull(startBearFromSwingLow);
UpdateBearFrom(bearStartFrom);
}
if(startBullFromSwingHigh >= 0) {
SwitchToBullFromBear(startBullFromSwingHigh);
UpdateBullFrom(bullStartFrom);
}
// NoBOS lines
if(noBosHigh >= 0)
DrawLine(K_B_NoBosHigh(), noBosHigh, bar.GetBarCount() - 1, bar.High(noBosHigh), "NO BOS", clrChartreuse, 1);
if(noBosLow >= 0)
DrawLine(K_S_NoBosLow(), noBosLow, bar.GetBarCount() - 1, bar.Low(noBosLow), "NO BOS", clrTomato, 1);
}
//+------------------------------------------------------------------+