188 lines
6.5 KiB
Plaintext
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__
|