2026-10-22 00:55:53 +08:00 | | | //+------------------------------------------------------------------+
|
| | | //| SignalAlerts.mqh |
|
| | | //| Copyright 2026, Allan Niñal |
|
| | | //| https://www.allanninal.dev/mql5 |
|
| | | //+------------------------------------------------------------------+
|
2026-10-31 08:12:02 +08:00 | | | //| Same alert semantics as Allan's signal indicators (UT Bot, |
|
| | | //| SuperTrend): one alert per closed-bar signal (pop-up, sound, |
|
| | | //| push, e-mail), never during the history load, never twice for |
|
| | | //| the same bar, silent in the tester. A scanner watches many |
|
| | | //| symbols, so the cells that signal in the same second are sent |
|
| | | //| as ONE message (push and e-mail are rate limited). |
|
2026-10-22 00:55:53 +08:00 | | | //+------------------------------------------------------------------+
|
| | | // SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
|
| | | #property strict
|
| | |
|
2026-10-31 08:12:02 +08:00 | | | #include <ChartKit/CkDebug.mqh>
|
| | |
|
2026-10-22 00:55:53 +08:00 | | | input group "Alerts"
|
| | | input bool InpAlertPopup = true; // Pop-up alert
|
| | | input bool InpAlertSound = false; // Play a sound
|
| | | input string InpSoundFile = "alert.wav"; // Sound file (terminal Sounds folder)
|
2026-10-31 14:28:30 +08:00 | | | input bool InpAlertPush = true; // Push notification (set up MetaQuotes ID in Options)
|
2026-10-22 00:55:53 +08:00 | | | input bool InpAlertEmail = false; // E-mail (set up in Options)
|
| | |
|
| | | //--- send one message to every channel that is switched on
|
| | | void SignalAlertMessage(const string product,const string msg)
|
| | | {
|
2026-10-31 08:12:02 +08:00 | | | if(msg=="")
|
| | | return;
|
| | | if(MQLInfoInteger(MQL_TESTER))
|
| | | {
|
| | | CkDbg("alert not sent in the Strategy Tester: "+msg);
|
2026-10-22 00:55:53 +08:00 | | | return;
|
2026-10-31 08:12:02 +08:00 | | | }
|
2026-10-22 00:55:53 +08:00 | | | string text=product+": "+msg;
|
2026-10-31 08:12:02 +08:00 | | | CkDbg("alert: "+msg);
|
2026-10-22 00:55:53 +08:00 | | | if(InpAlertPopup)
|
| | | Alert(text);
|
| | | if(InpAlertSound && InpSoundFile!="")
|
| | | PlaySound(InpSoundFile);
|
2026-10-31 08:12:02 +08:00 | | | if(InpAlertPush)
|
| | | {
|
| | | if(!TerminalInfoInteger(TERMINAL_NOTIFICATIONS_ENABLED))
|
| | | CkDbg("push not sent: notifications are off in Options");
|
| | | else
|
| | | if(!SendNotification(StringSubstr(text,0,255)))
|
| | | CkDbg("push failed, error "+IntegerToString(GetLastError()));
|
| | | }
|
| | | if(InpAlertEmail)
|
| | | {
|
| | | if(!TerminalInfoInteger(TERMINAL_EMAIL_ENABLED))
|
| | | CkDbg("e-mail not sent: e-mail is off in Options");
|
| | | else
|
| | | if(!SendMail(product,text))
|
| | | CkDbg("e-mail failed, error "+IntegerToString(GetLastError()));
|
| | | }
|
| | | }
|
| | |
|
| | | //--- Object budget: an indicator that draws labels or lines keeps at most OBJ_CAP of them. Past that, the oldest
|
| | | //--- are deleted as new ones are made, so a 20000-bar history cannot make the chart crawl. Buffers are not affected.
|
| | | #define OBJ_CAP 2500
|
| | | string g_objq[];
|
| | | int g_objn=0,g_objh=0;
|
| | | void ObjBudgetReset(void)
|
| | | {
|
| | | g_objn=0;
|
| | | g_objh=0;
|
| | | }
|
| | | void ObjBudget(const string name)
|
| | | {
|
| | | if(ArraySize(g_objq)!=OBJ_CAP)
|
| | | ArrayResize(g_objq,OBJ_CAP);
|
| | | if(g_objn<OBJ_CAP)
|
| | | {
|
| | | g_objq[(g_objh+g_objn)%OBJ_CAP]=name;
|
| | | g_objn++;
|
| | | return;
|
| | | }
|
| | | ObjectDelete(0,g_objq[g_objh]);
|
| | | g_objq[g_objh]=name;
|
| | | g_objh=(g_objh+1)%OBJ_CAP;
|
2026-10-22 00:55:53 +08:00 | | | }
|
| | | //+------------------------------------------------------------------+
|