Warrior_EA/Signals/SignalRiskGuard.mqh

56 lines
3.3 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| SignalRiskGuard.mqh |
//| AnimateDread |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "AnimateDread"
#property link "https://www.mql5.com"
#include "..\Expert\ExpertSignalCustom.mqh"
#include "..\Variables\RiskBudget.mqh"
//+------------------------------------------------------------------+
//| Class CSignalRiskGuard. |
//| 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. |
//+------------------------------------------------------------------+
class CSignalRiskGuard : public CExpertSignalCustom
{
public:
virtual double Direction(void);
};
//+------------------------------------------------------------------+
//| 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. |
//+------------------------------------------------------------------+
double CSignalRiskGuard::Direction(void)
{
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"
return 0.0;
}
//+------------------------------------------------------------------+