preview
From Novice to Expert: Backend Operations Monitor using MQL5

From Novice to Expert: Backend Operations Monitor using MQL5

MetaTrader 5Examples |
320 0
Clemence Benjamin
Clemence Benjamin

Discussion Contents:

  1. Introduction
  2. Implementation and Code
  3. Testing
  4. Conclusion
  5. Key lessons
  6. Attachments


Introduction

Successfully compiling a trading tool in MetaEditor 5 is an important milestone, but it is only the beginning. Compilation confirms that the code is syntactically valid, yet it does not guarantee performance, stability, or correctness. The real test begins when we run the system in MetaTrader 5, whether on the live platform or in the Strategy Tester, to see how theory translates into practice.

At this stage, developers face a common challenge: even though we wrote the logic, we do not always know how the EA will behave under real-time conditions. The program executes every rule mechanically, sometimes producing outcomes we did not expect. To refine and optimize our systems, we need more than just results—we need visibility into the inner workings of the EA, what is happening, when it is happening, and where in the code it originates.

The Limits of Built-in Logging and Our Solution

MetaTrader 5 provides two primary tools for monitoring activity:

  • The Experts tab, which displays logs generated by all running EAs and indicators.
  • The Journal tab, which records terminal and server events.

While these are useful, they have a critical limitation: all Expert Advisors share the same Experts tab. Even though log lines include the EA’s name, the outputs of multiple systems quickly become intermingled, creating clutter and confusion. When several EAs are active, it becomes difficult to isolate logs for one specific tool, slowing down debugging and reducing confidence in the results.

Here are sample expert log entries for three different programs listed together in the same sequence.

2025.09.21 08:10:42.006 BEODemoEA (EURAUD,H4)   Abnormal termination
2025.09.21 08:20:15.056 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURUSD' is not synchronized
2025.09.21 08:20:40.862 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURGBP' is not synchronized
2025.09.21 08:20:44.065 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURCHF' is not synchronized
2025.09.21 08:20:44.067 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURJPY' is not synchronized
2025.09.21 08:20:44.078 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPUSD' is not synchronized
2025.09.21 08:20:44.080 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPCHF' is not synchronized
2025.09.21 08:20:44.082 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPJPY' is not synchronized
2025.09.21 08:20:44.095 Correlation Matrix 3D (EURAUD,M5)       Symbol 'USDCHF' is not synchronized
2025.09.21 08:20:44.112 Correlation Matrix 3D (EURAUD,M5)       Symbol 'USDJPY' is not synchronized
2025.09.21 08:20:44.114 Correlation Matrix 3D (EURAUD,M5)       Symbol 'CHFJPY' is not synchronized
2025.09.21 08:20:44.114 Correlation Matrix 3D (EURAUD,M5)       Try again #0
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Some symbols not ready to use:
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURUSD 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURGBP 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPUSD 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       USDCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       USDJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       CHFJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Error. Symbols not synchronized.
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Error. Rates data is not loaded.
2025.09.21 09:00:00.112 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Session Alert: Sydney End at 2025.09.21 07:00
2025.09.21 10:00:00.177 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Session Alert: London Start at 2025.09.21 08:00
2025.09.21 10:05:00.172 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Bearish overlap, look to sell

This is where our discussion on the Backend Operations (BEO) class begins. Instead of relying solely on the shared Experts tab, we can design a dedicated monitoring and logging system that displays and organizes information directly on the chart. By building a class specifically for backend operations, developers gain;

  1. A clean, EA-specific debugging interface.
  2. Real-time visibility of performance metrics and internal events.
  3. Easier identification of errors, misbehavior, and execution flow.

In today’s article, we will explore how to design such a class—to strengthen our debugging workflow to give traders and developers a reliable tool for understanding what’s happening at the backend of their EAs.

Concept study

The feasibility of the Backend Operations (BEO) concept rests on whether MetaTrader 5 and MQL5 provide enough system hooks for monitoring and diagnostics inside an Expert Advisor. Fortunately, the platform exposes a rich set of functions for accessing account state, terminal environment, and trading operations. These range from AccountInfo*() and TerminalInfo*() for environment details to MqlTradeRequest and MqlTradeResult for trade execution outcomes. This ensures that any backend layer we build can always reference the same low-level information that the terminal itself relies on, making the concept technically sound.

Another critical factor is how these details are presented. MQL5 offers multiple output channels: the Experts tab for EA logs, the Journal tab for terminal and server messages, and chart-based visualization using Comment(), ObjectCreate(), or more advanced tools like CCanvas. Our custom class leverages these to redirect trade results, error codes, and diagnostic notes into structured outputs, allowing developers to distinguish one EA’s internal activity from another’s. This overcomes the limitation of mixed logs in the Experts tab, where multiple programs may otherwise blur into a single stream of messages.

Finally, integration feasibility is supported by the platform’s error handling and history functions. With GetLastError(), and access to history, our system layer can capture both real-time issues and past trade context, then display or log them in a developer-friendly manner. By organizing this into a dedicated backend class, we prove that the idea is achievable and also open the door to consistent debugging, easier upgrades, and transparent monitoring of trading systems on MetaTrader 5.

In the next section of our discussion, we will take this idea further by examining its implementation step by step. We will walk through the code details of the Backend Operations (BEO) class, highlighting how it captures, organizes, and displays essential debugging information. Once the foundation is clear, we will proceed to run on-chart tests, where the system will present real-time feedback directly on the trading chart.

Finally, we will share the complete solution so that developers and traders alike can integrate it into their own workflows, bridging the gap between theory, code, and practical execution.


Implementation and Code

The journey into backend operations begins with a structured approach that balances clarity and efficiency. By designing a dedicated monitoring class, CBEOMonitor, we can separate the responsibility of tracking and displaying diagnostics from the trading logic itself. This simplifies debugging and introduces a reusable building block that can be integrated into any Expert Advisor without rewriting the same routines again and again. The concept here is modularity—isolating one powerful unit of code to serve many different systems.

To bring this concept to life, I implemented it in two deliberate steps. First, the class itself was crafted with all the core mechanisms for capturing events, formatting information, and rendering it neatly on the chart. Then, integration into a demo EA showed how seamlessly such a class can plug into a live trading environment. This two-phase development emphasizes efficiency: one well-built tool can scale into increasingly complex systems. With that foundation set, it’s time to open up the code and see how each piece works in practice.

Step 1: The CBEOMonitor Class

Includes—what the monitor depends on and why

This monitor is built on top of MQL5’s UI primitives and standard containers. The #include lines bring in Canvas for bitmap-label rendering, List for a small in-memory ring buffer of messages, Object.mqh and ChartObject.mqh for chart-object constants and helpers, and Trade.mqh where trade-related helpers are used by the monitor’s logging helpers. The enums define the message severity levels and the display mode; they’re used everywhere for filtering, coloring, and choosing how many lines to show. Using small, explicit enums keeps the API readable and avoids magic numbers in the EA code. 

#include <Object.mqh>
#include <Canvas\Canvas.mqh>
#include <Arrays\List.mqh>
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObject.mqh>

// enums
enum BEO_Level { BEO_L_INFO=0, BEO_L_SUCCESS, BEO_L_WARNING, BEO_L_ERROR, BEO_L_TRADE, BEO_L_DEBUG };
enum BEO_DisplayMode { BEO_DM_COMPACT=0, BEO_DM_EXPANDED };

BEO_Item—the unit of logged information

Each log entry is represented by the BEO_Item class. It stores the human-readable text, a severity level, a datetime timestamp and a microsecond-resolution stamp for perf metrics, plus an optional origin label (where in the EA the message came from). Storing messages as objects allows a fixed capacity circular buffer (via CList) while keeping memory management straightforward. This small structure is the building block for presentation and for the experts log printouts. 

class BEO_Item : public CObject
{
public:
   string    text;
   BEO_Level level;
   datetime  time;
   ulong     micro;
   string    origin;

   BEO_Item() { text=""; level=BEO_L_INFO; time=0; micro=0; origin=""; }
   BEO_Item(string _text, BEO_Level _lvl, datetime _t, ulong _m, string _orig = "")
     { text=_text; level=_lvl; time=_t; micro=_m; origin=_orig; }
   void CopyFrom(const BEO_Item &src)
     { text=src.text; level=src.level; time=src.time; micro=src.micro; origin=src.origin; }
};

Monitor core state and color scheme—why opaque colors & solid text?

The monitor maintains its own CCanvas plus geometry, visibility, buffer, and performance counters. I chose to keep the main text fully opaque (solid white) and used distinct, opaque technical colors for caption, highlight, success, warning, and error messages. The goal: predictable legibility on any chart background and consistent meaning mapping (e.g., m_error = red). These colors are stored as color members and applied when drawing lines. Storing the font name and a clamped m_font_size protects against passing unsupported values to CCanvas::FontSizeSet() — that’s why we compute a safe CanvasFontSizeValue() before calling the Canvas API. 

// color and font members (excerpt)
color m_text;       // solid white
color m_caption;    // header/caption color
color m_error;      // error messages
string m_font;
int    m_font_size;
int    m_transparent; // canvas transparency 0..255
// CanvasFontSizeValue() clamps m_font_size into safe range used by CCanvas::FontSizeSet

Create/Recreate/Destroy—lifecycle with immediate visible placeholder

Creating the canvas immediately draws a small placeholder ("BEO engine initializing...") so users get instant visual feedback when the EA attaches—this is valuable for debugging attachment/creation issues. CreateBitmapLabel() is used to create the chart bitmap object, then FontNameSet()/FontSizeSet()/TransparentLevelSet() configure rendering. RecreateCanvas() safely destroys and recreates the canvas when position or size changes. Destroy() cleans up both the canvas and the in-memory message buffer; it also removes any fallback labels left over from earlier failures. These steps mirror good resource management principles from the MQL5 docs (create/update/destroy chart objects and canvas resources when required, avoid leaks). 

bool CBEOMonitor::Create(string name,int x,int y,int w,int h)
{
   m_res_name = StringFormat("BEOMON_%s", name);
   if(!m_canvas.CreateBitmapLabel(m_res_name, x, y, w, h, COLOR_FORMAT_ARGB_RAW)) return(false);
   m_canvas.FontNameSet(m_font);
   m_canvas.FontSizeSet(CanvasFontSizeValue());                     // safe/clamped
   m_canvas.TransparentLevelSet((uchar)MathMax(0, MathMin(255, m_transparent)));
   // placeholder UI so chart shows something immediately
   m_canvas.Erase(m_bg);
   m_canvas.FillRectangle(2,2,w-4,24, ARGB(255,44,44,44));
   m_canvas.TextOut(20,7,"BEO: " + name, m_caption, ALIGN_RIGHT);
   m_canvas.Update(true);
   return(true);
}

Logging, buffer trimming, and experts output—tracing both on-chart and in logs

When the EA calls Log(...) (or convenience wrappers like Info()/Error()), the monitor creates a BEO_Item, pushes it to the CList buffer, and emits a PrintFormat() message to the Experts log so the same message is visible in the standard Expert tab. The buffer is trimmed to a capacity to keep memory bounded. This dual approach—on-chart canvas plus Experts print—gives both fast visual diagnostics and a persistent textual record in the terminal log. 

void CBEOMonitor::Log(string text,BEO_Level lvl,string origin)
{
   if(lvl==BEO_L_DEBUG && !m_show_debug) return;
   BEO_Item it; it.text=text; it.level=lvl; it.time=TimeCurrent(); it.micro=_nowMicros(); it.origin=origin;
   _pushItem(it);
   PrintFormat("BEO[%s] %s%s", (lvl==BEO_L_INFO?"INFO": "..."), (origin!=""?("["+origin+"] "):""), text);
   Update();
}
void CBEOMonitor::_pushItem(const BEO_Item &it)
{
   BEO_Item *p = new BEO_Item; p.CopyFrom(it); m_buf.Add(p);
   while(m_buf.Total() > m_capacity) { BEO_Item *old=(BEO_Item*)m_buf.GetNodeAtIndex(0); if(old) delete old; m_buf.Delete(0); }
}

Performance timing—microsecond resolution for useful profiling

Call OnStartTick() at the start of tick processing and OnEndTick() at the end; internally the monitor uses GetMicrosecondCount() (on MQL5) or falls back to GetTickCount() multiplied for coarse resolution. It keeps a rolling average (m_tick_avg_ms) and a counter so the on-chart display shows how expensive tick processing is. This quick profiling is invaluable when tracking regressions after adding new features—if tick time suddenly spikes, the monitor shows it immediately. 

void CBEOMonitor::OnStartTick(void) { m_last_tick_start = _nowMicros(); }
void CBEOMonitor::OnEndTick(void)
{
   if(m_last_tick_start==0) return;
   ulong endm=_nowMicros();
   double durms=(double)((endm>m_last_tick_start)?(endm-m_last_tick_start):0)/1000.0;
   m_tick_avg_ms=(m_tick_avg_ms*m_tick_count+durms)/(m_tick_count+1);
   m_tick_count++;
   m_last_tick_start=0;
   Update();
}
ulong CBEOMonitor::_nowMicros(void) const { #ifdef __MQL5__ return (ulong)GetMicrosecondCount(); #else return (ulong)GetTickCount()*1000; #endif }

Drawing and clipping—readable, right-aligned output that won’t overflow

_draw() composes the canvas: border, caption, perf line, EA backend summary, and recent message lines. Each textual line is clipped via _clipLineToWidth() (which uses m_canvas.TextWidth() to measure pixels) to ensure no overflow or invisible text. All TextOut() calls use ALIGN_RIGHT as requested, and the main text color is solid white, while captions and highlights use dedicated opaque colors. After rendering, m_canvas.Update(true) flushes the bitmap to the chart, and BringToFrontInternal() reasserts chart object properties so the canvas remains visible above other objects. This approach keeps on-chart diagnostics consistent and readable across different chart sizes and resolutions. 

void CBEOMonitor::_draw(void)
{
   if(!m_canvas_created) return;
   m_canvas.Erase(m_bg);
   m_canvas.Rectangle(1,1,m_w-2,m_h-2,m_border);
   m_canvas.FillRectangle(2,2,m_w-4,22, ARGB(255,44,44,44));
   m_canvas.TextOut(20,6,"BEO: " + m_name, m_caption, ALIGN_RIGHT);
   // perf
   int y=28;
   string perf = StringFormat("TickAvg: %.2f ms | Ticks: %d | Mem: %d KB", m_tick_avg_ms, (int)m_tick_count, (int)m_mem_kb);
   m_canvas.TextOut(20,y, _clipLineToWidth(perf, MathMax(20,m_w-40)), m_text, ALIGN_RIGHT);
   y += 18;
   // messages (most recent first)
   for(int i=m_buf.Total()-1; i>=0 && drawn<max_lines; i--)
   {
      BEO_Item *it=_getItemAt(i);
      string line = _clipLineToWidth(TimeToString(it.time,TIME_MINUTES|TIME_SECONDS) + " " + it.text, avail_px);
      m_canvas.TextOut(20,y,line,_colorFor(it.level),ALIGN_RIGHT);
      y+=14;
      drawn++;
   }
   m_canvas.Update(true);
   BringToFrontInternal();
}

Step 2: Example integration of the CBEOMonitor into a custom demo EA 

EA Header and Includes

At the very top of the EA, we include all the core libraries that are essential for both trade execution and on-chart visualization. These include Trade.mqh to manage all trading operations, Controls\Button.mqh for creating interactive chart buttons, and BEOMonitor.mqh, which provides a robust monitoring system with structured logging and diagnostics.

Immediately after, we declare inputs so anyone attaching the EA can tune behavior without changing code. Inputs are developer-defined defaults the EA reads at OnInit(); they do not change code logic. Keep naming clear and defaults sensible. When using pixel/position inputs (MonitorW/MonitorH), remember the EA will pass them to beo.Create(...) at runtime, and the monitor will use them to create the canvas object.

#include <Trade\Trade.mqh>
#include <Controls\Button.mqh>
#include <BEOMonitor.mqh>

input string   DemoEAName        = "BEO Demo EA";
input bool     EnableMonitor     = true;
input int      MonitorX          = 10;
input int      MonitorY          = 60;
input int      MonitorW          = 400;
input int      MonitorH          = 220;
input bool     ShowDebugMessages = false;
input int      UpdateIntervalSec = 1;
input bool     AllowTrading      = false;
input double   DemoLotSize       = 0.01;
input double   DemoTakeProfit    = 30;
input double   DemoStopLoss      = 30;

Global Objects and Helpers

We define global instances for trading (CTrade), monitoring (CBEOMonitor), and our interactive buttons (CButton). These objects will manage their own lifecycle but are referenced throughout the EA. We also introduce helper functions for creating fallback labels. These labels act as a safety mechanism in case the monitor canvas fails to initialize. They provide immediate on-chart feedback so that we know the system is operational even if the graphical monitor cannot be displayed.

CTrade      trade;
CBEOMonitor beo;

CButton      m_buyButton;
CButton      m_sellButton;

static double g_prevBid = 0.0;
static double g_prevAsk = 0.0;
static datetime g_lastLogTime = 0;

string MakeFallbackName(const string base) { return ("BEO_FALLBACK_" + base); }

The fallback label functions (CreateFallbackLabel, UpdateFallbackLabel, RemoveFallbackLabel) allow it to dynamically show or update messages on the chart without interrupting the EA flow. They maintain visibility into the EA’s state, such as “initializing” or “canvas failed,” which is invaluable during testing and debugging.

Chart Diagnostics

To assist in monitoring, we implement a diagnostic function, PrintChartObjects(). This function iterates through all chart objects and prints their properties, including name, type, corner, position, and visibility. This is critical for troubleshooting resource conflicts or verifying that the BEO canvas is correctly created. It reflects our principle of transparency: knowing what exists on the chart prevents runtime surprises and aids in reproducibility of tests.

void PrintChartObjects()
{
   int total = ObjectsTotal(0);
   PrintFormat("BEO Diagnostic: ObjectsTotal = %d", total);
   for(int i=0;i<total;i++)
   {
      string nm = ObjectName(0,i);
      long type   = ObjectGetInteger(0,nm,OBJPROP_TYPE);
      long corner = ObjectGetInteger(0,nm,OBJPROP_CORNER);
      long xdist  = ObjectGetInteger(0,nm,OBJPROP_XDISTANCE);
      long ydist  = ObjectGetInteger(0,nm,OBJPROP_YDISTANCE);
      bool hidden = (ObjectGetInteger(0,nm,OBJPROP_HIDDEN) != 0);
      PrintFormat("  [%d] name='%s' type=%d corner=%d x=%d y=%d hidden=%d",
                  i, nm, (int)type, (int)corner, (int)xdist, (int)ydist, hidden ? 1 : 0);
   }
}

Initialization (OnInit)

In the OnInit function, we perform structured setup for both the monitor and quick order buttons. If monitoring is enabled, the program attempts to create the CBEOMonitor canvas. It then verifies its creation and, if successful, enables debugging and log a startup message. If canvas creation fails, the fallback label ensures that it still have on-chart feedback.

The EA also captures the initial bid and ask prices, using them as a baseline for tracking subsequent market changes. With this data, it constructs quick order buttons that allow manual trade simulation or execution. This mechanism provides a practical way to validate the trading logic interactively, instead of relying solely on automatic triggers. To ensure the monitoring display remains up to date, the EA also sets a timer that refreshes the canvas at regular intervals.

int OnInit()
{
   if(EnableMonitor)
   {
      bool created = beo.Create(DemoEAName, MonitorX, MonitorY, MonitorW, MonitorH);
      if(created && beo.IsCanvasCreated())
      {
         beo.EnableDebug(ShowDebugMessages);
         beo.Log("BEO engine initialized...", BEO_L_SUCCESS, "OnInit");
         RemoveFallbackLabel(DemoEAName);
      }
      else
      {
         CreateFallbackLabel(DemoEAName, MonitorX, MonitorY, 12, clrSilver);
      }
   }

   g_prevBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_prevAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(EnableQuickButtons)
   {
      // Create BUY and SELL buttons...
   }

   EventSetTimer(MathMax(1, UpdateIntervalSec));
   return(INIT_SUCCEEDED);
}

Deinitialization (OnDeinit)

During shutdown, the EA cleanly destroys the monitor canvas and quick buttons, while also removing any fallback labels. If parameters are modified, it skips unnecessary logging to enable seamless updates without interruption. By managing its resources in this way, the EA avoids cluttering the chart with unused objects or leaving dangling references behind. This careful cleanup reflects best practices in lifecycle management for automated trading systems.

void OnDeinit(const int reason)
{
   if(EnableMonitor && reason != REASON_PARAMETERS)
   {
      beo.Log("Shutting down demo EA", BEO_L_INFO, "OnDeinit");
      beo.ClearHistory();
      beo.Destroy();
      RemoveFallbackLabel(DemoEAName);
   }

   m_buyButton.Destroy(reason);
   m_sellButton.Destroy(reason);
   EventKillTimer();
}

Timer Updates (OnTimer)

The OnTimer event manages periodic updates. It ensures that the monitor canvas is recreated if missing, updates fallback labels, and refreshes the monitor display. Additionally, it logs a heartbeat every 30 seconds, providing insight into message count, tick performance, and system health. This regular update mechanism makes the EA robust against temporary chart or resource issues.

void OnTimer()
{
   if(!EnableMonitor) return;

   if(!beo.IsCanvasCreated())
   {
      static int tries = 0;
      tries++;
      bool ok = beo.Create(DemoEAName, MonitorX, MonitorY, MonitorW, MonitorH);
      if(ok) { /* recovered canvas */ }
      else
      {
         string txt = StringFormat("BEO fallback: msgs=%d | TickAvg=%.3f ms", beo.MessagesCount(), beo.GetTickAvgMs());
         UpdateFallbackLabel(DemoEAName, txt, MonitorX, MonitorY, 11, clrWhite);
      }
   }
   else RemoveFallbackLabel(DemoEAName);

   beo.Update(true);

   if(TimeCurrent() - g_lastLogTime >= 30)
   {
      string summary = StringFormat("Msgs:%d | TickAvg: %.3f ms | LastMicros: %llu",
                                    beo.MessagesCount(), beo.GetTickAvgMs(), beo.GetLastMicros());
      beo.Log("Periodic heartbeat: " + summary, BEO_L_DEBUG, "OnTimer");
      g_lastLogTime = TimeCurrent();
   }
}

Tick Processing (OnTick)

The OnTick function is the core reactive logic of the EA. At each market tick, the EA updates the monitor, tracks bid and ask changes, and logs relevant price movements. A simple demo strategy within the EA records significant price shifts and, when AllowTrading is enabled, can also execute trades. By splitting its logic into OnStartTick and OnEndTick, the EA preserves clear boundaries around monitor interactions, reinforcing modularity and improving readability.

void OnTick()
{
   if(EnableMonitor) beo.OnStartTick();

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(EnableMonitor && (bid != g_prevBid || ask != g_prevAsk))
   {
      string txt = StringFormat("Bid: %.*f Ask: %.*f",
                                (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS), bid,
                                (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS), ask);
      beo.Info(txt, "OnTick");
      g_prevBid = bid; g_prevAsk = ask;
   }

   // Simple demo strategy: log and optionally trade on price movement
}

Quick Order Buttons

The system handles user interactions through OnChartEvent and ExecuteQuickOrder. Clicking a button triggers either a simulated or real trade, depending on the AllowTrading flag. This allows us to demonstrate trading logic safely while fully integrating the Backend operations monitoring system, which captures detailed logs and trade events for review.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(sparam == "BEO_BUY_BTN") ExecuteQuickOrder(ORDER_TYPE_BUY);
      else if(sparam == "BEO_SELL_BTN") ExecuteQuickOrder(ORDER_TYPE_SELL);
   }
}

void ExecuteQuickOrder(ENUM_ORDER_TYPE orderType)
{
   if(!AllowTrading) { /* simulate */ return; }

   MqlTradeRequest req; MqlTradeResult res;
   ZeroMemory(req); ZeroMemory(res);

   if(orderType == ORDER_TYPE_BUY) { /* set buy parameters */ }
   else { /* set sell parameters */ }

   if(!OrderSend(req, res)) beo.LogErrorDetails(...);
   else beo.LogTradeDetails(res, ..., "QuickButton");
}

This approach demonstrates the full integration of CBEOMonitor; every major EA action—initialization, tick updates, timer events, trade execution, and button interaction—feeds structured logs, captures performance data, and allows fallback display when the monitor canvas fails. The system is modular, readable, and robust, ensuring clarity and maintainability for both live trading and testing.



Testing

To perform testing on the terminal, both the Expert Advisor and the header file containing the BEOMonitor class must have successfully compiled without errors. Under the Experts section in the MetaTrader 5 terminal navigator, the BEODemoEA will be available for use. Simply drag it onto the desired chart and allow it to initialize with the default settings. Once initialized, the system will automatically create the monitor canvas, set up the quick order buttons, and begin processing incoming tick data.

On the chart, the outcomes of the code execution become visible in real time. The canvas will display bid and ask updates according to the incoming ticks, while initialization messages confirm that the monitor and buttons were successfully created. At the same time, parallel logs in the Experts tab provide backend confirmation, making it possible to validate that the displayed information aligns with the internal EA operations. This direct visualization ensures that testing is not only functional but also interactive, giving immediate insight into the behavior of the system on live market data.

BEO testing on EURUSD

Figure 1: Deploying the EA on the chart

BEO testing

Figure 2: Showing the backend interaction with Quick Trade Buttons for simulated trading

MetaTrader 5 Terminal Experts logging:

2025.09.23 12:49:41.142 BEODemoEA (USDCHF,H1)   BEOMonitor: Created canvas resource 'BEOMON_BEO Demo EA' at 10,60 size 600x220
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: beo.Create returned true
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: beo.IsCanvasCreated() = true
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: expected canvas name = 'BEOMON_BEO Demo EA'
2025.09.23 12:49:41.178 BEODemoEA (USDCHF,H1)   BEO Diagnostic: ObjectsTotal = 3
2025.09.23 12:49:41.179 BEODemoEA (USDCHF,H1)     [0] name='BEOMON_BEO Demo EA' type=106 corner=0 x=10 y=60 hidden=0
2025.09.23 12:49:41.179 BEODemoEA (USDCHF,H1)     [1] name='BEO_BUY_BTN' type=103 corner=0 x=10 y=300 hidden=1
2025.09.23 12:49:41.180 BEODemoEA (USDCHF,H1)     [2] name='BEO_SELL_BTN' type=103 corner=0 x=100 y=300 hidden=1
2025.09.23 12:49:41.196 BEODemoEA (USDCHF,H1)   BEO[OK] [OnInit] BEO engine initialized...
2025.09.23 12:49:41.228 BEODemoEA (USDCHF,H1)   BEO[INFO] [OnInit] Quick Order Buttons (CButton) created: BUY/SELL for ops testing
2025.09.23 12:49:59.941 BEODemoEA (USDCHF,H1)   BEO[INFO] [OnTick] Bid: 0.79537 Ask: 0.79551

The diagnostic entries above confirm that the canvas was initialized at the intended coordinates and dimensions, and validation checks such as beo.Create and beo.IsCanvasCreated() returned true. This indicates that the integration between the custom monitor class and the charting environment is stable. Furthermore, the expected canvas name was correctly matched, ensuring proper resource identification. The diagnostic summary also enumerated the related objects, including the hidden BUY and SELL quick order buttons, showing that the system handles its main display component and manages auxiliary controls consistently.

From a functional standpoint, the initialization logs demonstrate that the BEO engine reliably enters an operational state and that the quick order testing buttons are generated as intended, even though hidden for this stage. The real-time OnTick feed confirms the ability to capture and render market data through the monitor, with bid and ask prices streaming live into the canvas space.

This proves the concept, we can now visualize backend Expert Advisor activity in its own custom environment rather than relying solely on the terminal’s text logs. While the foundation is strong, future improvements should focus on refining the presentation layer, enhancing interaction with the quick order controls, and expanding the scope of diagnostic feedback to include more complex EA operations. This marks a promising start, validating the architecture and offering a solid platform for iterative development.



Conclusion

We have successfully brought the BEO concept to life, with the canvas now rendering at customizable dimensions and dynamically displaying Expert Advisor backend operations in real time. Along the way, we faced a few challenges. Initially, text visibility was poor because we chose a transparent color that blended into the background; this was later corrected with a solid, brighter color for clarity.

Alignment also proved tricky—our first attempts caused text to overshoot the left border. By switching to ALIGN_RIGHT, we achieved a clean presentation, even though conventionally we expect left alignment in word processors. With these fixes, the display on the chart became both functional and visually coherent, and—most importantly—it is not static: tick-by-tick backend activity of the EA is actively displayed.

The result validates the idea, but it is only a foundation. We solved a key problem by giving the EA its own specialized log space, separated from the crowded terminal Experts tab, while still recognizing that the terminal offers convenient log copying for debugging purposes. At present, we can visualize important backend details directly on chart, but future refinements could extend functionality, richer customization of log data, advanced filtering, or even interactive copying of logs from the canvas. The work here lays a strong base, but there is room for growth. Presented in this form, the project shows what is possible, yet future updates may push it much further.

Explore, experiment, and expand this idea in your own projects. A short table of key lessons and the attached code complete this discussion—your comments and contributions will help shape what comes next.

Key lessons

LessonDescription:
Custom Class Development in MQL5Building a dedicated class such as CBEOMonitor demonstrates how complex functionality can be encapsulated into reusable modules, improving maintainability and scalability of trading systems.
Integration of Custom Classes and Built-in MQL5 Library ClassesCombining user-defined classes with standard library components like CTrade and CButton shows the flexibility of MQL5 in blending custom features with reliable built-in functionality.
Canvas IntegrationUsing a custom canvas allows Expert Advisors to display backend operations visually on the chart, creating a dedicated space for monitoring without depending on the terminal log window.
Color and Visibility ManagementChoosing appropriate solid colors instead of transparent ones is essential for clarity, ensuring that diagnostic text and data remain visible under different chart backgrounds.
Text AlignmentPrecise alignment prevents overshooting or misplacement of displayed text. Understanding how ALIGN_LEFT, ALIGN_RIGHT, and chart offsets behave helps to maintain a clean presentation.
Fallback MechanismsImplementing labels as fallback when canvas creation fails ensures robustness. This provides at least basic status feedback even if advanced resources cannot be initialized.
Real-Time DiagnosticsBy logging tick data, spread changes, and trade signals directly on the chart, the Expert Advisor creates a live feedback loop that makes debugging and performance validation easier.

Attachments

File nameVersionDescription
BEOMonitor.mqh1.00Core monitoring class that manages the canvas, logs, and visual diagnostics of Expert Advisor backend operations. Provides reusable methods for drawing, logging, and structured output on a chart.
BEODemoEA.mq51.00Demonstration Expert Advisor integrating CBEOMonitor to showcase live logging, diagnostic visualization, and quick-order testing with customizable controls.
Attached files |
BEOMonitor.mqh (48.41 KB)
BEODemoEA.mq5 (27.26 KB)
Automating Trading Strategies in MQL5 (Part 35): Creating a Breaker Block Trading System Automating Trading Strategies in MQL5 (Part 35): Creating a Breaker Block Trading System
In this article, we create a Breaker Block Trading System in MQL5 that identifies consolidation ranges, detects breakouts, and validates breaker blocks with swing points to trade retests with defined risk parameters. The system visualizes order and breaker blocks with dynamic labels and arrows, supporting automated trading and trailing stops.
MQL5 Trading Tools (Part 9): Developing a First Run User Setup Wizard for Expert Advisors with Scrollable Guide MQL5 Trading Tools (Part 9): Developing a First Run User Setup Wizard for Expert Advisors with Scrollable Guide
In this article, we develop an MQL5 First Run User Setup Wizard for Expert Advisors, featuring a scrollable guide with an interactive dashboard, dynamic text formatting, and visual controls like buttons and a checkbox allowing users to navigate instructions and configure trading parameters efficiently. Users of the program get to have insight of what the program is all about and what to do on the first run, more like an orientation model.
Features of Experts Advisors Features of Experts Advisors
Creation of expert advisors in the MetaTrader trading system has a number of features.
Price Action Analysis Toolkit Development (Part 42): Interactive Chart Testing with Button Logic and Statistical Levels Price Action Analysis Toolkit Development (Part 42): Interactive Chart Testing with Button Logic and Statistical Levels
In a world where speed and precision matter, analysis tools need to be as smart as the markets we trade. This article presents an EA built on button logic—an interactive system that instantly transforms raw price data into meaningful statistical levels. With a single click, it calculates and displays mean, deviation, percentiles, and more, turning advanced analytics into clear on-chart signals. It highlights the zones where price is most likely to bounce, retrace, or break, making analysis both faster and more practical.