
Today’s discussion focuses on addressing the challenges associated with delivering trading reports in MetaTrader 5. In our previous publication, we outlined the overall workflow and requirements for the system to function effectively. We introduced the Reporting EA as a tool designed to generate and deliver trading reports in PDF format, with a frequency that can be customized by the user.
In this follow-up, our goal is to build upon that foundation by enhancing both the depth of information contained in the reports and the efficiency of their delivery. This improvement is made possible through the integration of MQL5 with powerful Python libraries, enabling richer outputs and smoother automation. Along the way, we will uncover valuable lessons on how cross-language integration can be leveraged to create robust and seamless trading solutions.
This topic is worth pursuing once you recognize how impactful detailed reporting can be on a trader’s mindset and decision-making process. In the previous version, the generated PDF included only four data points — a clear limitation and a strong reminder of the need for more comprehensive reporting. Going forward, our reports will incorporate expanded metrics, visualizations, graphs, and multiple analytical components, as outlined in paragraph 2 of the earlier article about understanding the reporting features.
Ultimately, the aim is to deliver a flexible and information-rich reporting tool that every analyst or trader can adapt to their unique trading strategies and workflow.
Figure 1: Reports screenshot from the trade report produced by the early version of the Reporting EA
The image above was taken from the PDF report generated by the first version of our reports_processor.py script. The corresponding code snippet is shared below. Today, our focus is on refining this code so that it produces more detailed and insightful reports.
Through this discussion, we will explore additional capabilities that Python can bring to the reporting process and demonstrate how to streamline the interaction between MQL5 and Python for greater efficiency and flexibility.
When we compare the reporting features shown in Figure 1 above and Figure 2 below, it becomes clear that there is still significant room for improvement. Our objective is not to reinvent existing solutions, but to provide a customizable and flexible alternative for generating trading reports.
The image from the MetaTrader 5 terminal’s reporting section illustrates the current features, which we will use as a baseline for the enhancements we plan to implement.
Figure 3 (flow diagram) below presents the processing workflow to be implemented by the Reporting EA update. The EA exports a dated CSV of trading history and launches the Python report generator, which processes the CSV to compute analytics, produce charts, and write the final report (PDF) plus a compact JSON result containing pdf_path and email_sent status. The EA polls for that JSON, reads and parses it, validates the reported output paths and file sizes, and — on successful validation — sends notifications and optionally archives or opens the report to complete the automated reporting cycle.
At this stage we will implement the MQL5 side of the update to deliver high-quality report output. We’ll break the EA into logical sections and explain the code that directly supports our reporting goals. Remember that MQL5 Expert Advisors are not limited to placing trades — they can automate many routine trading tasks, and reporting is a perfect example. Although our current focus is a lightweight, dedicated reporting EA, the same reporting module can be integrated later into a more feature-rich advisor. Designing a specialized, focused EA lets us decompose a large problem into manageable pieces, build and refine the reporting feature in isolation, validate it through smoke tests and targeted debugging, and then plug the polished component into more sophisticated systems with minimal drawbacks.
1. Header, Windows imports, and user inputs
This section declares the EA metadata, imports the minimal Windows API calls we require (to test absolute-file attributes and to launch cmd.exe), and exposes user-configurable inputs. Keep these values accurate for your environment (Python path, script path, output directory) so the EA can validate them at startup. The ReportFormats, EnableEmail, and TestOnInit inputs let you control behavior without code changes.
2. Initialization and environment validation (OnInit)
OnInit() warms up the EA, seeds the RNG, cleans the configured paths, logs the terminal files directory, verifies the Python executable and output directory exist, and confirms write permissions to MQL5Files. When TestOnInit is enabled, it triggers a test run — a helpful smoke check to confirm the end-to-end chain is wired correctly.
3. Path utilities and basic helpers
Clean, normalized paths and robust file checks avoid a whole class of runtime errors. CleanPath() normalizes slashes and warns about spaces; FileExists() wraps GetFileAttributesW for absolute paths and falls back to FileOpen for terminal-local files. Keep these helpers central and reuse them throughout the EA.
4. CSV creation and export logic
ExportHistoryToCSV() handles history selection and writes a CSV with a header and per-deal rows. It uses MakeUniqueFilename() to avoid collisions (useful for repeated runs) and returns the chosen filename in the global LastExportedCSV. You want your CSV writer to be deterministic and to include the columns the Python script expects.
5. Python invocation and orchestration (RunDailyExport)
RunDailyExport() is the orchestration hub: it builds the CSV path, launches Python with cmd.exe and redirects stdout/stderr into python_run.log in MQL5Files so the EA can still inspect the script output. The method then polls for the preferred report_result_YYYY-MM-DD.json (produced by Python) and falls back to PDF polling if JSON isn’t available. This design prioritizes a structured JSON handshake so the EA can reliably confirm success and attachments.
6. JSON handling and fallback read strategy
To avoid brittle reliance on human-readable logs, the EA prefers a machine-readable JSON result. ReadFileText() attempts to read absolute JSON paths directly; when MetaTrader 5 refuses absolute FileOpen, CopyToFilesAndRead() uses a cmd copy to copy the JSON into MQL5Files and then reads it with FileOpen. JsonExtractString / JsonExtractBool are small, pragmatic extractors sufficient for the simple JSON contract (pdf_path, email_sent).
7. Verification, notification, and graceful fallback
After parsing the JSON, the EA validates the pdf_path and html_path values with FileExists() and optionally notifies the trader via SendNotification(). If JSON read or validation fails the EA gracefully falls back to tailing python_run.log and polling for the expected Report_YYYY-MM-DD.pdf (size > 0). This layered approach balances reliability (JSON) with robustness (PDF/log fallback).
8. Support helpers and teardown
Utility routines such as GetFileSizeByOpen() (safe file size check), ReadTerminalLogFile() (read files from MQL5Files), and the ShellExecute() wrapper are small but important. OnDeinit() prints a message on cleanup. Keep these compact and well-documented; they simplify debugging and reduce duplication.
The EA works hand-in-hand with the Python reports_processor.py script, so now that we have updated the EA we must adapt the Python side to save and report results in a way that the EA can consume efficiently. In this section we will explain the script code in detail and show exactly how each part integrates with the EA — from CSV ingestion and analytics through PDF rendering and JSON result emission — to ensure a reliable, automatable reporting pipeline.
Before diving into the breakdown of our script code, we first need to set up the Python environment to ensure the system can fully support the script’s functionality. In the following steps, we will walk through the setup process starting from installation. Completing this crucial step gives us confidence that the environment is properly configured, allowing us to seamlessly perform tests during the development of the reports_processor script.
Step-by-Step Guide: Setting Up Python on Windows for the reports_processor Script
1. Install Python 3.9+ from python.org and ensure python is on PATH.
4. Install core packages (fpdf is default PDF backend; add others if desired):
Optional backends:
If using wkhtmltopdf, download its Windows installer and add the executable to PATH or set WKHTMLTOPDF_PATH env var.
5. Set environment variables for email (example: set in System Properties → Environment Variables or PowerShell):
On startup the script loads Python standard libraries (os, sys, logging, datetime, argparse, glob) and core data libraries (pandas, numpy). It then attempts to import optional toolchains — WeasyPrint, pdfkit (wkhtmltopdf), FPDF, and yagmail — and records availability flags for each backend. These flags let the script pick an appropriate PDF generation or emailing route at runtime based on what is installed, while still running in environments where some optional packages are absent. Logging is configured early so the script emits timestamped informational and error messages that the EA can capture in python_run.log.
2. Argument parsing and command contract
The script exposes a small CLI: its positional csv_path accepts either a CSV file path or a folder to search; –formats controls which outputs to create (HTML, PDF, or both); –outdir sets where to write artifacts; –email toggles sending reports; and –pdf-backend selects which PDF path to use. This contract matches the EA invocation pattern: the EA provides the CSV path, the output directory, and an optional email flag, and then monitors the output directory for a JSON result.
3. CSV resolution (finding the input file)
When given a folder or a flexible csv_path, the script resolves the actual CSV to process. If csv_path is a directory, it searches for History_*.csv files (falling back to any .csv if none match) and picks the most recently modified one. If a full CSV path is provided, it is accepted as-is. This makes the script tolerant and easy to call both from the EA (which passes a specific CSV) and for manual runs.
4. Loading and normalizing trade data
The loader reads the CSV using pandas, attempting to parse a time column into datetimes. It ensures required columns exist (for example profit, symbol, balance, and equity) by filling defaults or computing values when possible (e.g., cumulative sums for balance/equity when needed). After normalization, the DataFrame is sorted by time, producing a consistent, time-ordered dataset for analytics and plotting.
5. Computing analytics and metrics
The compute_stats step derives the key report metrics the EA and the user care about: a report date string, net profit, total trade count, the top-performing symbol (by aggregated profit), maximum drawdown computed from the equity curve, and a simple Sharpe-like ratio estimate. These values are packaged into a stats dictionary and later embedded into both HTML templates and PDF pages to present a quick executive summary.
6. Charting — equity / balance curve
The script produces an equity curve PNG using matplotlib. It prefers plotting time vs balance and equity when timestamps are present; otherwise, it plots index vs. balance/equity. The generated PNG is saved into the –outdir and later referenced by the HTML template or embedded into the PDF, giving readers a visual summary of account performance over the reporting window.
7. HTML rendering with Jinja2
If HTML output is requested (or produced as an intermediate artifact), the script uses Jinja2 to render report_template.html. The template receives the stats dictionary, the per-symbol profit aggregates, and the curve image filename. The rendered HTML is written to the output folder and logged. This HTML can be used as the final deliverable or as the source input for HTML-to-PDF converters.
8. PDF generation — multiple backends
The script supports three PDF generation strategies and selects one based on the –pdf-backend argument and detected availability:
Each backend writes the final Report_YYYY-MM-DD.pdf file into the configured output directory and reports its completion via logging.
9. Email sending
If the –email flag is set, the script uses an email helper routine to send the generated report files to the configured recipient using the available emailing library. The routine logs whether sending succeeded, and the result is captured later in the JSON handshake so the EA can determine whether an email was dispatched during that run.
10. Result JSON writer — the EA handshake
After generating outputs, the script compiles a compact, machine-readable JSON containing pdf_path, html_path, email_sent, timestamp, and an exit_code. The JSON is written atomically (via a temporary file + rename) into the –outdir as report_result_YYYY-MM-DD.json. Because the write is atomic, the EA can safely poll for the presence of the JSON file as a reliable signal that the Python side finished and then read its contents to learn the exact output locations and whether emailing occurred.
11. Main flow
The main() function strings everything together: it parses arguments, resolves the CSV, loads & normalizes the data, computes stats, generates the equity chart, renders HTML (if requested), generates PDF(s) via the selected backend, optionally sends email, cleans up older reports in the output directory, writes the result JSON, and exits with a success or failure code. The sequence and logging ensure that the EA — which launched the script — can follow the run’s progress (via the python_run.log) and react to the final report_result_…json.
12. Error handling and guaranteed JSON on failure
Throughout main(), exceptions are caught at the top level so that if any unhandled error occurs, the script logs the traceback and still writes a report_result_YYYY-MM-DD.json with an exit_code indicating failure. This ensures the EA always receives a deterministic JSON object to inspect (success or failure), which simplifies the EA’s verification and fallback logic.
13. Cleanup and retention policy
At the end of a successful run, the script performs a retention sweep of the output directory, removing older Report_ files beyond a configurable window (the current script uses 30 days). This keeps the output folder tidy and prevents unbounded disk growth on systems that run reports frequently.
How the EA and Python script integrate at runtime
The runtime handshake is straightforward: the EA exports a CSV and launches the script (passing the CSV path, –outdir, and any flags). The Python script processes the CSV and writes artifacts (PNG, PDF) into the output folder and finally writes report_result_YYYY-MM-DD.json. The EA polls for that JSON; once present, it reads the JSON, validates the reported paths and file sizes, and performs post-actions (notifications, archiving, email confirmation). The logging (python_run.log) and the JSON together provide a robust, machine-consumable contract between the two components.
Our testing is carried out by deploying the EA on the MetaTrader 5 platform, where the Python script is launched and executed in the background. The results can be monitored through the Experts Log in the terminal, as well as by checking the designated output directories for generated files. In my test, I located the output report named Report_2025-08-21. The contents of this file are far more extensive than those produced in earlier versions. The report now includes well-structured tables, equity curves, and multiple calculated metrics, all presented in a clear format that provides valuable insights for traders. Below are screenshots showcasing the generated report files and partly the PDF contents.
Figure 5: Animated screenshot from the reports PDF generated
We have successfully demonstrated the integration of MQL5 and Python to deliver feature-rich trading reports that go far beyond the basic outputs provided by the previous version of our Reporting EA. One of the standout features is the automated equity curve, which visually tracks balance and equity trends over time, offering traders an immediate grasp of performance dynamics. The system proves the feasibility of linking an Expert Advisor with an external Python reporting pipeline and also showcases how this collaboration can be refined and expanded further.
By continuously refining the EA code and leveraging powerful Python libraries such as Pandas, Matplotlib, and FPDF, we can compute advanced statistics, generate insightful tables, and embed clear visualizations into reports. The addition of a lightweight JSON handshake between the EA and the Python script ensures reliable confirmation of outputs, making the workflow seamless and transparent. Furthermore, optional features such as automated email delivery and flexible PDF backends demonstrate the adaptability of the framework to different environments.
This development lays a solid foundation for long-term reporting solutions. The resulting documents can serve as comprehensive tools for trading performance analysis, helping traders and investors alike to spot trends, measure risks such as drawdowns, and evaluate strategies with greater clarity. Ultimately, the Reporting EA combined with the reports_processor.py script shows how cross-language integration can enhance automation, improve decision-making, and open the door to even more sophisticated analytics in the future.

