Files
2026-05-28 19:46:22 +03:00

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__