Initialize project in MT5 Experts directory
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Data/FibonacciEngine.mqh |
|
||||
//| Fibonacci Retracement & Extension Analysis |
|
||||
//| Identifies key S/R levels: 0.236, 0.382, 0.5, 0.618, 0.786 |
|
||||
//| Uses swing highs/lows for accurate level placement |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __FIBONACCI_ENGINE_MQH__
|
||||
#define __FIBONACCI_ENGINE_MQH__
|
||||
|
||||
#include "../Core/Config.mqh"
|
||||
#include "../Core/State.mqh"
|
||||
#include "../Data/PriceEngine.mqh"
|
||||
|
||||
class CFibonacciEngine
|
||||
{
|
||||
private:
|
||||
ENUM_TIMEFRAMES m_tf;
|
||||
double m_levels[7]; // 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0
|
||||
double m_levelValues[7];
|
||||
bool m_levelsValid;
|
||||
double m_swingHigh;
|
||||
double m_swingLow;
|
||||
datetime m_swingHighTime;
|
||||
datetime m_swingLowTime;
|
||||
|
||||
public:
|
||||
CFibonacciEngine() : m_levelsValid(false), m_swingHigh(0), m_swingLow(0) {}
|
||||
|
||||
bool Init(ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
m_tf = tf;
|
||||
m_levels[0] = 0.0;
|
||||
m_levels[1] = 0.236;
|
||||
m_levels[2] = 0.382;
|
||||
m_levels[3] = 0.500;
|
||||
m_levels[4] = 0.618;
|
||||
m_levels[5] = 0.786;
|
||||
m_levels[6] = 1.0;
|
||||
Print("[FibonacciEngine] Initialized on ", EnumToString(tf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Calculate()
|
||||
{
|
||||
// Find significant swing high and low
|
||||
FindSwingPoints();
|
||||
|
||||
if(m_swingHigh <= m_swingLow || m_swingHigh == 0 || m_swingLow == 0)
|
||||
{
|
||||
m_levelsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
double range = m_swingHigh - m_swingLow;
|
||||
for(int i = 0; i < 7; i++)
|
||||
{
|
||||
m_levelValues[i] = m_swingHigh - (range * m_levels[i]);
|
||||
}
|
||||
m_levelsValid = true;
|
||||
}
|
||||
|
||||
// Check if price is near a key fibonacci level (for entries)
|
||||
bool IsNearFibLevel(double price, double tolerance, int &nearestLevel)
|
||||
{
|
||||
if(!m_levelsValid) return false;
|
||||
|
||||
nearestLevel = -1;
|
||||
double minDist = DBL_MAX;
|
||||
|
||||
// Most important levels for entries: 0.382, 0.5, 0.618, 0.786
|
||||
int keyLevels[] = {2, 3, 4, 5};
|
||||
|
||||
for(int i = 0; i < ArraySize(keyLevels); i++)
|
||||
{
|
||||
int idx = keyLevels[i];
|
||||
double dist = MathAbs(price - m_levelValues[idx]);
|
||||
if(dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearestLevel = idx;
|
||||
}
|
||||
}
|
||||
|
||||
double atr = iATR(_Symbol, m_tf, 14);
|
||||
if(atr == 0) atr = _Point * 50;
|
||||
|
||||
return (minDist <= tolerance * atr);
|
||||
}
|
||||
|
||||
// Get the strongest level (0.618 golden ratio)
|
||||
double GetGoldenRatioLevel() const
|
||||
{
|
||||
if(!m_levelsValid) return 0;
|
||||
return m_levelValues[4]; // 0.618
|
||||
}
|
||||
|
||||
// Get 0.786 level (deep retracement - final support/resistance)
|
||||
double GetDeepLevel() const
|
||||
{
|
||||
if(!m_levelsValid) return 0;
|
||||
return m_levelValues[5]; // 0.786
|
||||
}
|
||||
|
||||
// Check if price broke a fib level (trend continuation signal)
|
||||
bool DidBreakLevel(double prevClose, double currClose, int levelIdx)
|
||||
{
|
||||
if(!m_levelsValid || levelIdx < 0 || levelIdx >= 7) return false;
|
||||
|
||||
double level = m_levelValues[levelIdx];
|
||||
return ((prevClose < level && currClose > level) ||
|
||||
(prevClose > level && currClose < level));
|
||||
}
|
||||
|
||||
// Get fibonacci extension for TP calculation
|
||||
double GetExtension(double multiplier)
|
||||
{
|
||||
if(!m_levelsValid) return 0;
|
||||
double range = m_swingHigh - m_swingLow;
|
||||
return m_swingHigh + (range * multiplier);
|
||||
}
|
||||
|
||||
string GetLevelName(int idx) const
|
||||
{
|
||||
if(idx < 0 || idx >= 7) return "Invalid";
|
||||
string names[] = {"0.0", "0.236", "0.382", "0.5", "0.618", "0.786", "1.0"};
|
||||
return names[idx];
|
||||
}
|
||||
|
||||
bool IsValid() const { return m_levelsValid; }
|
||||
double GetSwingHigh() const { return m_swingHigh; }
|
||||
double GetSwingLow() const { return m_swingLow; }
|
||||
|
||||
private:
|
||||
void FindSwingPoints()
|
||||
{
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
int copied = CopyRates(_Symbol, m_tf, 0, 100, rates);
|
||||
if(copied < 20) { m_levelsValid = false; return; }
|
||||
|
||||
// Find swing high (highest high in last 50 bars)
|
||||
m_swingHigh = 0;
|
||||
m_swingHighTime = 0;
|
||||
int highIdx = iHighest(_Symbol, m_tf, MODE_HIGH, 50, 1);
|
||||
if(highIdx >= 0)
|
||||
{
|
||||
m_swingHigh = iHigh(_Symbol, m_tf, highIdx);
|
||||
m_swingHighTime = iTime(_Symbol, m_tf, highIdx);
|
||||
}
|
||||
|
||||
// Find swing low (lowest low in last 50 bars)
|
||||
m_swingLow = DBL_MAX;
|
||||
m_swingLowTime = 0;
|
||||
int lowIdx = iLowest(_Symbol, m_tf, MODE_LOW, 50, 1);
|
||||
if(lowIdx >= 0)
|
||||
{
|
||||
m_swingLow = iLow(_Symbol, m_tf, lowIdx);
|
||||
m_swingLowTime = iTime(_Symbol, m_tf, lowIdx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __FIBONACCI_ENGINE_MQH__
|
||||
@@ -0,0 +1,306 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Data/LiquidityEngine.mqh |
|
||||
//| Smart Money Concepts: Order Blocks, Liquidity Sweeps, FVG |
|
||||
//| Identifies institutional levels for high-probability entries |
|
||||
//+------------------------------------------------------------------+
|
||||
#ifndef __LIQUIDITY_ENGINE_MQH__
|
||||
#define __LIQUIDITY_ENGINE_MQH__
|
||||
|
||||
#include "../Core/Config.mqh"
|
||||
#include "../Core/State.mqh"
|
||||
|
||||
struct OrderBlock
|
||||
{
|
||||
double high;
|
||||
double low;
|
||||
double open;
|
||||
double close;
|
||||
datetime time;
|
||||
bool isBullish; // true = bullish OB (buy zone)
|
||||
bool isValid;
|
||||
int strength; // 1-3 based on volume and follow-through
|
||||
};
|
||||
|
||||
struct LiquidityPool
|
||||
{
|
||||
double level;
|
||||
datetime time;
|
||||
bool isBuySide; // true = buy-side liquidity (equal highs)
|
||||
bool isSwept; // true = liquidity was swept/taken
|
||||
int touchCount; // how many times price touched this level
|
||||
};
|
||||
|
||||
class CLiquidityEngine
|
||||
{
|
||||
private:
|
||||
ENUM_TIMEFRAMES m_tf;
|
||||
OrderBlock m_bullishOBs[];
|
||||
OrderBlock m_bearishOBs[];
|
||||
LiquidityPool m_pools[];
|
||||
int m_maxOBs;
|
||||
int m_lookback;
|
||||
|
||||
public:
|
||||
CLiquidityEngine() : m_maxOBs(5), m_lookback(50) {}
|
||||
|
||||
bool Init(ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
m_tf = tf;
|
||||
ArrayResize(m_bullishOBs, m_maxOBs);
|
||||
ArrayResize(m_bearishOBs, m_maxOBs);
|
||||
ArrayResize(m_pools, 10);
|
||||
Print("[LiquidityEngine] Initialized on ", EnumToString(tf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
FindOrderBlocks();
|
||||
FindLiquidityPools();
|
||||
}
|
||||
|
||||
// Check if price is at a valid order block
|
||||
bool IsAtOrderBlock(double price, bool wantBullish, OrderBlock &outOB)
|
||||
{
|
||||
if(wantBullish)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(m_bullishOBs); i++)
|
||||
{
|
||||
if(!m_bullishOBs[i].isValid) continue;
|
||||
if(price >= m_bullishOBs[i].low && price <= m_bullishOBs[i].high)
|
||||
{
|
||||
outOB = m_bullishOBs[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < ArraySize(m_bearishOBs); i++)
|
||||
{
|
||||
if(!m_bearishOBs[i].isValid) continue;
|
||||
if(price >= m_bearishOBs[i].low && price <= m_bearishOBs[i].high)
|
||||
{
|
||||
outOB = m_bearishOBs[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for liquidity sweep (stop hunt) - reversal signal
|
||||
bool WasLiquiditySwept(int barsBack, bool &sweptBuySide)
|
||||
{
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
if(CopyRates(_Symbol, m_tf, 0, barsBack + 5, rates) < barsBack + 5) return false;
|
||||
|
||||
// Check for sweep of equal highs/lows
|
||||
double recentHigh = 0, recentLow = DBL_MAX;
|
||||
for(int i = 1; i <= barsBack; i++)
|
||||
{
|
||||
if(rates[i].high > recentHigh) recentHigh = rates[i].high;
|
||||
if(rates[i].low < recentLow) recentLow = rates[i].low;
|
||||
}
|
||||
|
||||
// Buy-side liquidity sweep (swept highs then reversed down)
|
||||
if(rates[0].high > recentHigh && rates[0].close < rates[1].close)
|
||||
{
|
||||
sweptBuySide = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sell-side liquidity sweep (swept lows then reversed up)
|
||||
if(rates[0].low < recentLow && rates[0].close > rates[1].close)
|
||||
{
|
||||
sweptBuySide = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for Fair Value Gap (FVG) - imbalance zone
|
||||
bool HasFVG(int barsBack, bool &isBullishFVG, double &fvgTop, double &fvgBottom)
|
||||
{
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
if(CopyRates(_Symbol, m_tf, 0, barsBack + 3, rates) < barsBack + 3) return false;
|
||||
|
||||
for(int i = 1; i < barsBack; i++)
|
||||
{
|
||||
// Bullish FVG: current low > previous high (gap up)
|
||||
if(rates[i].low > rates[i+1].high)
|
||||
{
|
||||
isBullishFVG = true;
|
||||
fvgTop = rates[i].low;
|
||||
fvgBottom = rates[i+1].high;
|
||||
return true;
|
||||
}
|
||||
// Bearish FVG: current high < previous low (gap down)
|
||||
if(rates[i].high < rates[i+1].low)
|
||||
{
|
||||
isBullishFVG = false;
|
||||
fvgTop = rates[i+1].low;
|
||||
fvgBottom = rates[i].high;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the nearest untapped liquidity level
|
||||
double GetNearestLiquidity(double currentPrice, bool above)
|
||||
{
|
||||
double nearest = 0;
|
||||
double minDist = DBL_MAX;
|
||||
|
||||
for(int i = 0; i < ArraySize(m_pools); i++)
|
||||
{
|
||||
if(m_pools[i].isSwept) continue;
|
||||
|
||||
if(above && m_pools[i].level > currentPrice)
|
||||
{
|
||||
double dist = m_pools[i].level - currentPrice;
|
||||
if(dist < minDist) { minDist = dist; nearest = m_pools[i].level; }
|
||||
}
|
||||
else if(!above && m_pools[i].level < currentPrice)
|
||||
{
|
||||
double dist = currentPrice - m_pools[i].level;
|
||||
if(dist < minDist) { minDist = dist; nearest = m_pools[i].level; }
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private:
|
||||
void FindOrderBlocks()
|
||||
{
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
int copied = CopyRates(_Symbol, m_tf, 0, m_lookback, rates);
|
||||
if(copied < 10) return;
|
||||
|
||||
int bullCount = 0, bearCount = 0;
|
||||
|
||||
for(int i = 2; i < copied - 1 && (bullCount < m_maxOBs || bearCount < m_maxOBs); i++)
|
||||
{
|
||||
// Bullish Order Block: bearish candle before strong bullish move
|
||||
if(rates[i].close < rates[i].open && rates[i-1].close > rates[i-1].open * 1.01)
|
||||
{
|
||||
// Strong bullish follow-through
|
||||
if(bullCount < m_maxOBs)
|
||||
{
|
||||
m_bullishOBs[bullCount].high = rates[i].high;
|
||||
m_bullishOBs[bullCount].low = rates[i].low;
|
||||
m_bullishOBs[bullCount].open = rates[i].open;
|
||||
m_bullishOBs[bullCount].close = rates[i].close;
|
||||
m_bullishOBs[bullCount].time = rates[i].time;
|
||||
m_bullishOBs[bullCount].isBullish = true;
|
||||
m_bullishOBs[bullCount].isValid = true;
|
||||
m_bullishOBs[bullCount].strength = CalculateStrength(rates, i);
|
||||
bullCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Bearish Order Block: bullish candle before strong bearish move
|
||||
if(rates[i].close > rates[i].open && rates[i-1].close < rates[i-1].open * 0.99)
|
||||
{
|
||||
// Strong bearish follow-through
|
||||
if(bearCount < m_maxOBs)
|
||||
{
|
||||
m_bearishOBs[bearCount].high = rates[i].high;
|
||||
m_bearishOBs[bearCount].low = rates[i].low;
|
||||
m_bearishOBs[bearCount].open = rates[i].open;
|
||||
m_bearishOBs[bearCount].close = rates[i].close;
|
||||
m_bearishOBs[bearCount].time = rates[i].time;
|
||||
m_bearishOBs[bearCount].isBullish = false;
|
||||
m_bearishOBs[bearCount].isValid = true;
|
||||
m_bearishOBs[bearCount].strength = CalculateStrength(rates, i);
|
||||
bearCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindLiquidityPools()
|
||||
{
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
int copied = CopyRates(_Symbol, m_tf, 0, m_lookback, rates);
|
||||
if(copied < 20) return;
|
||||
|
||||
int poolCount = 0;
|
||||
|
||||
// Find equal highs (buy-side liquidity)
|
||||
for(int i = 5; i < copied - 5 && poolCount < 10; i++)
|
||||
{
|
||||
double currHigh = rates[i].high;
|
||||
bool isEqualHigh = false;
|
||||
|
||||
for(int j = i + 2; j < i + 10 && j < copied; j++)
|
||||
{
|
||||
if(MathAbs(rates[j].high - currHigh) < _Point * 10)
|
||||
{
|
||||
isEqualHigh = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(isEqualHigh)
|
||||
{
|
||||
m_pools[poolCount].level = currHigh;
|
||||
m_pools[poolCount].time = rates[i].time;
|
||||
m_pools[poolCount].isBuySide = true;
|
||||
m_pools[poolCount].isSwept = (rates[0].high > currHigh + _Point * 5);
|
||||
m_pools[poolCount].touchCount = 2;
|
||||
poolCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Find equal lows (sell-side liquidity)
|
||||
for(int i = 5; i < copied - 5 && poolCount < 10; i++)
|
||||
{
|
||||
double currLow = rates[i].low;
|
||||
bool isEqualLow = false;
|
||||
|
||||
for(int j = i + 2; j < i + 10 && j < copied; j++)
|
||||
{
|
||||
if(MathAbs(rates[j].low - currLow) < _Point * 10)
|
||||
{
|
||||
isEqualLow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(isEqualLow)
|
||||
{
|
||||
m_pools[poolCount].level = currLow;
|
||||
m_pools[poolCount].time = rates[i].time;
|
||||
m_pools[poolCount].isBuySide = false;
|
||||
m_pools[poolCount].isSwept = (rates[0].low < currLow - _Point * 5);
|
||||
m_pools[poolCount].touchCount = 2;
|
||||
poolCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CalculateStrength(MqlRates &rates[], int idx)
|
||||
{
|
||||
int strength = 1;
|
||||
|
||||
// Volume check
|
||||
double avgVol = 0;
|
||||
for(int i = idx; i < idx + 5 && i < ArraySize(rates); i++)
|
||||
avgVol += (double)rates[i].tick_volume;
|
||||
avgVol /= 5.0;
|
||||
|
||||
if(rates[idx].tick_volume > avgVol * 1.5) strength++;
|
||||
if(rates[idx].tick_volume > avgVol * 2.0) strength++;
|
||||
|
||||
return MathMin(strength, 3);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __LIQUIDITY_ENGINE_MQH__
|
||||
Reference in New Issue
Block a user