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

188 lines
6.5 KiB
Plaintext

//+------------------------------------------------------------------+
//| Risk/Protection.mqh |
//| Enhanced Circuit Breakers with Dynamic Risk Adjustment |
//| Reduces position size after consecutive losses |
//+------------------------------------------------------------------+
#ifndef __PROTECTION_MQH__
#define __PROTECTION_MQH__
#include "../Core/Config.mqh"
#include "../Core/State.mqh"
#include "../Core/Logger.mqh"
extern CLogger g_logger;
class CProtection
{
private:
double m_maxDailyLoss;
double m_maxWeeklyLoss;
int m_maxConsecLosses;
int m_maxPositions;
double m_maxTotalRisk;
datetime m_lastDailyReset;
datetime m_lastWeeklyReset;
double m_lastEquity;
int m_consecLossCounter;
datetime m_lastTradeTime;
double m_currentRiskMultiplier; // Dynamic risk reduction
public:
bool Init(double dailyLoss, double weeklyLoss, int consecLoss, int maxPos, double maxRisk)
{
m_maxDailyLoss = dailyLoss;
m_maxWeeklyLoss = weeklyLoss;
m_maxConsecLosses = consecLoss;
m_maxPositions = maxPos;
m_maxTotalRisk = maxRisk;
m_lastDailyReset = 0;
m_lastWeeklyReset = 0;
m_lastEquity = AccountInfoDouble(ACCOUNT_EQUITY);
m_consecLossCounter = 0;
m_lastTradeTime = 0;
m_currentRiskMultiplier = 1.0;
Print("[Protection] Circuit breakers active. Daily:", dailyLoss, "% Weekly:", weeklyLoss, "% Consec:", consecLoss);
return true;
}
bool IsCircuitBreakerActive(EAState &state) const
{
if(TimeCurrent() < state.circuitBreakerUntil)
return true;
if(state.circuitBreakerUntil > 0 && TimeCurrent() >= state.circuitBreakerUntil)
{
g_logger.LogEvent("PROTECTION", "Circuit breaker expired. Trading resumed.");
state.circuitBreakerUntil = 0;
state.circuitBreakerReason = "";
state.dailyLimitHit = false;
state.weeklyLimitHit = false;
state.consecLossHalted = false;
}
return false;
}
bool PreTradeCheck(EAState &state) const
{
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
if(equity <= 0) return false;
double dailyLimit = equity * (m_maxDailyLoss / 100.0);
if(state.dailyPnL <= -dailyLimit)
{
ActivateBreaker(state, "Daily Loss Limit", 86400);
state.dailyLimitHit = true;
return false;
}
double weeklyLimit = equity * (m_maxWeeklyLoss / 100.0);
if(state.weeklyPnL <= -weeklyLimit)
{
ActivateBreaker(state, "Weekly Loss Limit", 7 * 86400);
state.weeklyLimitHit = true;
return false;
}
if(state.consecutiveLosses >= m_maxConsecLosses)
{
ActivateBreaker(state, "Consecutive Losses", 86400);
state.consecLossHalted = true;
return false;
}
if(state.openPositions >= m_maxPositions)
return false;
return true;
}
bool IsSpreadAcceptable(const AssetProfile &profile) const
{
if(!InpUseSpreadFilter) return true;
long spreadPoints = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
double spreadPrice = spreadPoints * _Point;
return (spreadPrice <= profile.maxSpreadPoints);
}
// Get dynamic risk multiplier based on recent performance
double GetRiskMultiplier() const
{
return m_currentRiskMultiplier;
}
void UpdateState(EAState &state)
{
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
if(m_lastEquity > 0 && currentEquity != m_lastEquity)
{
double equityChange = currentEquity - m_lastEquity;
state.dailyPnL += equityChange;
state.weeklyPnL += equityChange;
if(equityChange < 0)
{
if(TimeCurrent() != m_lastTradeTime)
{
m_consecLossCounter++;
state.consecutiveLosses = m_consecLossCounter;
m_lastTradeTime = TimeCurrent();
// Dynamic risk reduction after consecutive losses
if(m_consecLossCounter == 1) m_currentRiskMultiplier = 0.75;
else if(m_consecLossCounter == 2) m_currentRiskMultiplier = 0.50;
else if(m_consecLossCounter >= 3) m_currentRiskMultiplier = 0.25;
g_logger.LogEvent("PROTECTION", StringFormat("Loss detected. Consecutive: %d/%d. Risk multiplier: %.2f",
m_consecLossCounter, m_maxConsecLosses, m_currentRiskMultiplier));
}
}
else if(equityChange > 0)
{
if(m_consecLossCounter > 0)
{
m_consecLossCounter = 0;
state.consecutiveLosses = 0;
m_currentRiskMultiplier = 1.0; // Reset to full risk
g_logger.LogEvent("PROTECTION", "Profit detected. Risk multiplier reset to 1.0");
}
}
}
m_lastEquity = currentEquity;
if(InpDebugMode)
{
g_logger.LogEvent("PROTECTION", StringFormat("State | Daily: %.2f | Weekly: %.2f | Consec: %d | RiskMult: %.2f | Equity: %.2f",
state.dailyPnL, state.weeklyPnL, state.consecutiveLosses, m_currentRiskMultiplier, currentEquity));
}
}
void CheckDailyReset(EAState &state)
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
datetime todayStart = StringToTime(StringFormat("%04d.%02d.%02d 00:00:00", dt.year, dt.mon, dt.day));
if(todayStart > m_lastDailyReset)
{
state.dailyPnL = 0;
state.totalTradesToday = 0;
state.consecutiveLosses = 0;
m_consecLossCounter = 0;
m_currentRiskMultiplier = 1.0;
m_lastDailyReset = todayStart;
state.equityAtStart = AccountInfoDouble(ACCOUNT_EQUITY);
m_lastEquity = state.equityAtStart;
g_logger.LogEvent("PROTECTION", "Daily counters reset. Risk multiplier reset to 1.0");
}
if(dt.day_of_week == 1 && todayStart > m_lastWeeklyReset)
{
state.weeklyPnL = 0;
state.totalTradesWeek = 0;
m_lastWeeklyReset = todayStart;
state.equityAtWeekStart = AccountInfoDouble(ACCOUNT_EQUITY);
g_logger.LogEvent("PROTECTION", "Weekly counters reset");
}
}
private:
void ActivateBreaker(EAState &state, string reason, int seconds) const
{
state.circuitBreakerUntil = TimeCurrent() + seconds;
state.circuitBreakerReason = reason;
g_logger.LogEvent("PROTECTION", StringFormat("CIRCUIT BREAKER: %s. Halted for %d sec.", reason, seconds));
}
};
#endif // __PROTECTION_MQH__