2026-07-18 17:29:38 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| SignalRiskGuard.mqh |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "AnimateDread"
|
|
|
|
|
#property link "https://www.mql5.com"
|
|
|
|
|
#include "..\Expert\ExpertSignalCustom.mqh"
|
2026-08-02 12:25:20 -04:00
|
|
|
#include "..\Variables\RiskBudget.mqh"
|
2026-07-18 17:29:38 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Class CSignalRiskGuard. |
|
2026-08-02 12:25:20 -04:00
|
|
|
//| Vetoes new entries (the EMPTY_VALUE convention every other filter |
|
|
|
|
|
//| here uses, via the composite Direction()) whenever the account's |
|
|
|
|
|
//| loss budget is exhausted. |
|
|
|
|
|
//| |
|
|
|
|
|
//| This class USED TO OWN the whole mechanism - thresholds, day-start|
|
|
|
|
|
//| balance, peak equity, its own state file. It no longer does, and |
|
|
|
|
|
//| the reason is that a CExpertSignal filter is the wrong place for |
|
|
|
|
|
//| an account-level hard limit: |
|
|
|
|
|
//| |
|
|
|
|
|
//| * Direction() is called by the signal pipeline, and with the |
|
|
|
|
|
//| shipped Expert_EveryTick=false that is ONCE PER BAR at the bar|
|
|
|
|
|
//| open. A 4% daily limit checked once an hour on H1 is not a |
|
|
|
|
|
//| limit. The budget is now evaluated from OnTick()/OnTimer() |
|
|
|
|
|
//| (Variables\RiskBudget.mqh), at quote frequency, whatever |
|
|
|
|
|
//| timeframe the signals run on. |
|
|
|
|
|
//| * A veto can only decline to OPEN. It cannot size a trade to |
|
|
|
|
|
//| fit the remaining allowance, and it cannot act on a position |
|
|
|
|
|
//| that is already running toward the limit. Both of those now |
|
|
|
|
|
//| live in CRiskBudget, which the money manager consults before |
|
|
|
|
|
//| every lot-size calculation. |
|
|
|
|
|
//| |
|
|
|
|
|
//| What remains here is the entry veto alone - kept because the |
|
|
|
|
|
//| composite-Direction convention is how every other filter blocks a |
|
|
|
|
|
//| trade, so the journal, the panel and the vote arithmetic all |
|
|
|
|
|
//| behave consistently when the budget halts trading. |
|
2026-07-18 17:29:38 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CSignalRiskGuard : public CExpertSignalCustom
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
virtual double Direction(void);
|
|
|
|
|
};
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-02 12:25:20 -04:00
|
|
|
//| CRiskBudget::Update() has already run this tick (OnTick calls it |
|
|
|
|
|
//| before Expert.OnTick()), so this is a pure read - no account |
|
|
|
|
|
//| queries, no file I/O, nothing that costs anything per bar. |
|
2026-07-18 17:29:38 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CSignalRiskGuard::Direction(void)
|
|
|
|
|
{
|
2026-08-02 12:25:20 -04:00
|
|
|
if(!g_riskBudget.Enabled())
|
|
|
|
|
return 0.0; // permanent no-op, same convention as the other disabled filters
|
|
|
|
|
if(g_riskBudget.Halted())
|
|
|
|
|
return EMPTY_VALUE; // veto: the composite Direction() turns this into "no new entries"
|
2026-07-18 17:29:38 -04:00
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|