309 lines
9.7 KiB
Plaintext
309 lines
9.7 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| Logic/MicroTrigger.mqh |
|
|
//| Enhanced LTF Entry Logic with Fibonacci, Liquidity, Price Action |
|
|
//| Higher probability entries using multiple confluences |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef __MICRO_TRIGGER_MQH__
|
|
#define __MICRO_TRIGGER_MQH__
|
|
|
|
#include "../Core/Config.mqh"
|
|
#include "../Core/State.mqh"
|
|
#include "../Data/PriceEngine.mqh"
|
|
#include "../Data/Volatility.mqh"
|
|
#include "../Data/FibonacciEngine.mqh"
|
|
#include "../Data/LiquidityEngine.mqh"
|
|
#include "../Core/Logger.mqh"
|
|
|
|
extern CLogger g_logger;
|
|
extern CVolatility g_volatility;
|
|
|
|
class CMicroTrigger
|
|
{
|
|
private:
|
|
ENUM_TIMEFRAMES m_ltf;
|
|
CPriceEngine *m_price;
|
|
CFibonacciEngine m_fib;
|
|
CLiquidityEngine m_liquidity;
|
|
|
|
public:
|
|
bool Init(ENUM_TIMEFRAMES ltf, CPriceEngine &price)
|
|
{
|
|
m_ltf = ltf;
|
|
m_price = GetPointer(price);
|
|
|
|
if(!m_fib.Init(ltf))
|
|
{
|
|
Print("[MicroTrigger] FibonacciEngine init failed");
|
|
return false;
|
|
}
|
|
|
|
if(!m_liquidity.Init(ltf))
|
|
{
|
|
Print("[MicroTrigger] LiquidityEngine init failed");
|
|
return false;
|
|
}
|
|
|
|
Print("[MicroTrigger] LTF entry logic initialized on ", EnumToString(ltf), " (with Fib + Liquidity)");
|
|
return true;
|
|
}
|
|
|
|
void Release() {}
|
|
|
|
void GenerateSignal(SignalData &signal, const EAState &state, CPriceEngine &price)
|
|
{
|
|
signal.isValid = false;
|
|
signal.isBuy = false;
|
|
signal.pattern = PATTERN_NONE;
|
|
signal.rejectionReason = "";
|
|
signal.signalTime = TimeCurrent();
|
|
signal.atrValue = 0;
|
|
|
|
// Update fibonacci and liquidity levels
|
|
m_fib.Calculate();
|
|
m_liquidity.Update();
|
|
|
|
// Check HTF bias validity
|
|
if(state.currentBias == BIAS_NEUTRAL && state.currentRegime != REGIME_RANGE)
|
|
{
|
|
signal.rejectionReason = "HTF Bias Neutral + Not Range Mode";
|
|
return;
|
|
}
|
|
|
|
MqlRates bars[4];
|
|
if(!price.GetClosedBar(m_ltf, 1, bars[1]) || !price.GetClosedBar(m_ltf, 2, bars[2]))
|
|
{
|
|
signal.rejectionReason = "Failed to load LTF closed bars";
|
|
return;
|
|
}
|
|
|
|
// === CONFLUENCE SCORING SYSTEM ===
|
|
// Each confluence adds to score. Need minimum score for valid signal.
|
|
int confluenceScore = 0;
|
|
bool isBuy = false;
|
|
ENUM_PATTERN detectedPattern = PATTERN_NONE;
|
|
string patternName = "";
|
|
|
|
// 1. Check Price Action Patterns (0-3 points)
|
|
if(CheckPinBar(bars[1], state))
|
|
{
|
|
detectedPattern = PATTERN_PIN_BAR;
|
|
patternName = "Pin Bar";
|
|
isBuy = (bars[1].close > bars[1].open);
|
|
confluenceScore += 2;
|
|
}
|
|
else if(CheckEngulfing(bars[1], bars[2]))
|
|
{
|
|
detectedPattern = PATTERN_ENGULFING;
|
|
patternName = "Engulfing";
|
|
isBuy = (bars[1].close > bars[1].open);
|
|
confluenceScore += 2;
|
|
}
|
|
else if(price.GetClosedBar(m_ltf, 3, bars[3]) && CheckInsideBarBreakout(bars[1], bars[2], bars[3]))
|
|
{
|
|
detectedPattern = PATTERN_INSIDE_BAR;
|
|
patternName = "Inside Bar Breakout";
|
|
isBuy = (bars[1].close > bars[2].high);
|
|
confluenceScore += 1;
|
|
}
|
|
|
|
if(confluenceScore == 0)
|
|
{
|
|
signal.rejectionReason = "No valid price action pattern";
|
|
return;
|
|
}
|
|
|
|
// 2. Check Fibonacci Level (0-2 points)
|
|
int fibLevel = -1;
|
|
if(m_fib.IsNearFibLevel(bars[1].close, 0.5, fibLevel))
|
|
{
|
|
confluenceScore += 2;
|
|
patternName += " + Fib" + m_fib.GetLevelName(fibLevel);
|
|
}
|
|
else if(m_fib.IsNearFibLevel(bars[1].close, 1.0, fibLevel))
|
|
{
|
|
confluenceScore += 1;
|
|
patternName += " + Fib" + m_fib.GetLevelName(fibLevel);
|
|
}
|
|
|
|
// 3. Check Order Block (0-2 points)
|
|
OrderBlock ob;
|
|
if(m_liquidity.IsAtOrderBlock(bars[1].close, isBuy, ob))
|
|
{
|
|
confluenceScore += ob.strength;
|
|
patternName += " + OB";
|
|
}
|
|
|
|
// 4. Check Liquidity Sweep (0-3 points) - STRONG signal
|
|
bool sweptBuySide;
|
|
if(m_liquidity.WasLiquiditySwept(3, sweptBuySide))
|
|
{
|
|
// If liquidity was swept and we're trading in opposite direction
|
|
if((isBuy && !sweptBuySide) || (!isBuy && sweptBuySide))
|
|
{
|
|
confluenceScore += 3;
|
|
patternName += " + Liquidity Sweep";
|
|
}
|
|
}
|
|
|
|
// 5. Check FVG (Fair Value Gap) (0-1 points)
|
|
bool isBullishFVG;
|
|
double fvgTop, fvgBottom;
|
|
if(m_liquidity.HasFVG(10, isBullishFVG, fvgTop, fvgBottom))
|
|
{
|
|
if((isBuy && isBullishFVG) || (!isBuy && !isBullishFVG))
|
|
{
|
|
confluenceScore += 1;
|
|
patternName += " + FVG";
|
|
}
|
|
}
|
|
|
|
// === VALIDATION ===
|
|
// Minimum confluence score required
|
|
int minScore = (state.currentRegime == REGIME_TREND) ? 4 : 3;
|
|
|
|
if(confluenceScore < minScore)
|
|
{
|
|
signal.rejectionReason = StringFormat("Confluence score %d < minimum %d", confluenceScore, minScore);
|
|
return;
|
|
}
|
|
|
|
// Direction validation
|
|
if(!ValidateDirection(isBuy, state))
|
|
{
|
|
signal.rejectionReason = isBuy ? "Bullish signal rejected (HTF Bias: BEAR)" : "Bearish signal rejected (HTF Bias: BULL)";
|
|
return;
|
|
}
|
|
|
|
// Build signal
|
|
signal.isValid = true;
|
|
signal.isBuy = isBuy;
|
|
signal.pattern = detectedPattern;
|
|
signal.patternName = patternName + StringFormat(" [Score:%d]", confluenceScore);
|
|
|
|
CalculateLevels(signal, bars[1], state);
|
|
}
|
|
|
|
private:
|
|
bool ValidateDirection(bool isBuy, const EAState &state)
|
|
{
|
|
if(state.currentRegime == REGIME_RANGE) return true;
|
|
if(state.currentBias == BIAS_BULL && !isBuy) return false;
|
|
if(state.currentBias == BIAS_BEAR && isBuy) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CheckPinBar(const MqlRates &bar, const EAState &state)
|
|
{
|
|
double body = MathAbs(bar.close - bar.open);
|
|
double upperWick = bar.high - MathMax(bar.open, bar.close);
|
|
double lowerWick = MathMin(bar.open, bar.close) - bar.low;
|
|
double range = bar.high - bar.low;
|
|
if(range == 0 || body == 0) return false;
|
|
|
|
bool bullish = (bar.close > bar.open);
|
|
if(bullish)
|
|
{
|
|
bool wickOK = (lowerWick >= body * PIN_BAR_WICK_MULT);
|
|
bool closePos = (bar.close >= bar.low + range * 0.7);
|
|
bool atLevel = IsAtKeyLevel(bar, state, true);
|
|
return wickOK && closePos && atLevel;
|
|
}
|
|
else
|
|
{
|
|
bool wickOK = (upperWick >= body * PIN_BAR_WICK_MULT);
|
|
bool closePos = (bar.close <= bar.low + range * 0.3);
|
|
bool atLevel = IsAtKeyLevel(bar, state, false);
|
|
return wickOK && closePos && atLevel;
|
|
}
|
|
}
|
|
|
|
bool CheckEngulfing(const MqlRates &curr, const MqlRates &prev)
|
|
{
|
|
bool bullish = (curr.close > prev.open && curr.open < prev.close);
|
|
bool bearish = (curr.close < prev.open && curr.open > prev.close);
|
|
if(!bullish && !bearish) return false;
|
|
return (curr.tick_volume >= prev.tick_volume * ENGULF_VOLUME_MULT);
|
|
}
|
|
|
|
bool CheckInsideBarBreakout(const MqlRates &breakout, const MqlRates &inside, const MqlRates &mother)
|
|
{
|
|
bool isInside = (inside.high < mother.high && inside.low > mother.low);
|
|
if(!isInside) return false;
|
|
bool bullBreak = (breakout.close > inside.high);
|
|
bool bearBreak = (breakout.close < inside.low);
|
|
return (bullBreak || bearBreak);
|
|
}
|
|
|
|
bool IsAtKeyLevel(const MqlRates &bar, const EAState &state, bool isBullish)
|
|
{
|
|
double proximity = state.assetProfile.atrMultiplierSL * g_volatility.GetATR() * 0.5;
|
|
if(MathAbs(bar.close - state.vwapState.vwapValue) <= proximity) return true;
|
|
if(isBullish && MathAbs(bar.low - state.swingLow) <= proximity) return true;
|
|
if(!isBullish && MathAbs(bar.high - state.swingHigh) <= proximity) return true;
|
|
|
|
// Check fibonacci levels
|
|
int fibIdx;
|
|
if(m_fib.IsNearFibLevel(bar.close, 0.3, fibIdx)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void CalculateLevels(SignalData &signal, const MqlRates &bar, const EAState &state)
|
|
{
|
|
double atr = g_volatility.GetATR();
|
|
if(atr <= 0) atr = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * 10;
|
|
signal.atrValue = atr;
|
|
|
|
if(signal.isBuy) signal.entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
else signal.entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
double slMult, tp1Mult, tp2Mult;
|
|
if(state.currentRegime == REGIME_TREND)
|
|
{
|
|
slMult = InpTrendATRMult;
|
|
tp1Mult = InpTrendATRMult * 2.0;
|
|
tp2Mult = InpTrendATRMult * 4.0;
|
|
}
|
|
else
|
|
{
|
|
slMult = InpRangeATRMult;
|
|
tp1Mult = InpRangeATRMult * 1.5;
|
|
tp2Mult = InpRangeATRMult * 2.5;
|
|
}
|
|
|
|
double slDist = atr * slMult;
|
|
double tp1Dist = atr * tp1Mult;
|
|
double tp2Dist = atr * tp2Mult;
|
|
|
|
// Adjust TP based on fibonacci extensions if available
|
|
if(m_fib.IsValid())
|
|
{
|
|
double fibExtension = m_fib.GetExtension(1.618);
|
|
if(fibExtension > 0)
|
|
{
|
|
if(signal.isBuy && fibExtension > signal.entryPrice + tp1Dist)
|
|
tp2Dist = fibExtension - signal.entryPrice;
|
|
else if(!signal.isBuy && fibExtension < signal.entryPrice - tp1Dist)
|
|
tp2Dist = signal.entryPrice - fibExtension;
|
|
}
|
|
}
|
|
|
|
if(signal.isBuy)
|
|
{
|
|
signal.slPrice = signal.entryPrice - slDist;
|
|
signal.tp1Price = signal.entryPrice + tp1Dist;
|
|
signal.tp2Price = signal.entryPrice + tp2Dist;
|
|
}
|
|
else
|
|
{
|
|
signal.slPrice = signal.entryPrice + slDist;
|
|
signal.tp1Price = signal.entryPrice - tp1Dist;
|
|
signal.tp2Price = signal.entryPrice - tp2Dist;
|
|
}
|
|
|
|
signal.isValid = true;
|
|
}
|
|
};
|
|
|
|
#endif // __MICRO_TRIGGER_MQH__
|