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;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|