• MQL5 90.3%
  • MQL4 9.7%
Find a file
2026-06-11 16:41:04 -07:00
src refactor: pull up prepareBeforeCalculating to base class 2026-05-19 20:07:14 -07:00
.gitattributes Initial commit 2026-02-20 15:09:43 -08:00
Evolution.mq5 feat: reconfigure plots and buffers 2026-05-12 18:24:14 -07:00
README.md docs: update README.md 2026-06-11 16:41:04 -07:00

Evolution

Article Link

This repository contains the companion source code for the article Modular Indicator Architecture in MQL5 (Part 1): Stop Copy-Pasting and Start Writing Scalable, Reusable Code.

It demonstrates how to refactor standard procedural MQL5 indicators into a robust, object-oriented (OOP), and modular framework.

📖 Learning via Single-Purpose Commits

The educational material in the original article is uniquely built around the single-purpose Git commits of this very repository. Rather than presenting massive, monolithic code dumps, the article serves as a guided tour through the project's actual commit history, where every architectural change is isolated into a specific, atomic step.

Readers are strongly encouraged to follow along using the commit links provided in the article. As shown in the image below, these links act as captions for code snippets and lead directly to the corresponding commits that built the codebase you see here. Clicking these links reveals the exact line-by-line diffs (what was added in green, what was removed in red), which is the absolute best way to grasp the logic behind the changes and see exactly how the architecture evolved from a basic script to a robust framework.

Code snippet caption with a link to the corresponding Git commit
Example from the article: Each code snippet is accompanied by a direct link to the corresponding Git commit, allowing readers to view the exact line-by-line diffs.

📰 Original Article

🧠 Difficulty

Intermediate to Advanced

The article assumes familiarity with:

  • MQL5 Basics: Procedural indicator development (e.g., OnCalculate, indicator buffers).
  • Object-Oriented Programming (OOP): Core concepts such as classes, inheritance, and encapsulation are required to fully grasp the architectural refactoring.
  • Git Fundamentals: Understanding what commits are and how to read line-by-line diffs (additions/deletions), as the learning process heavily relies on navigating Git history.

🎯 Target Audience

It is useful for readers who want to study:

  • How to transition from procedural, monolithic code to a clean, object-oriented architecture in MQL5.
  • How to utilize core OOP principles (encapsulation, inheritance, polymorphism) to build modular and reusable indicator components.
  • How to properly organize and structure MQL5 projects using header files (.mqh) for better maintainability.
  • Practical techniques for eliminating global state and decoupling input parameters from core calculation logic.
  • The step-by-step process of refactoring basic scripts into a scalable, professional-grade framework.

💡 Key Concepts

  • Encapsulation & Zero Global State: Moving away from global arrays and variables. The entire indicator's state and logic are encapsulated within a single root CIndicator object, minimizing the global footprint to just a single pointer.
  • The "Sub-Indicator" Paradigm (CSubIndiBase): A modular approach where complex indicators are decomposed into smaller, independent calculation units (e.g., CSma, CAppliedPrice). Every sub-indicator manages its own indicator buffers, calculation loops, and state, adhering to the Single Responsibility Principle.
  • The Registry Pattern (CSubIndiRegistry): A smart, centralized dependency management system. The static registry automatically calculates and tracks the maximum barsRequired across all registered sub-indicators, ensuring that the main calculation loop only initiates when sufficient historical data is present.
  • Input Parameter Isolation (CIndiParamsBase): Decoupling global MQL5 input variables from the core business logic. Settings are bundled into parameter structs or classes and passed down the object hierarchy, preventing tight coupling and "spaghetti" data flows.
  • Infrastructure Abstraction: Routine boilerplate code, such as checking rates_total or managing buffer initialization, is extracted into base classes (CIndicatorBase, CSubIndiBase). This keeps the derived concrete classes clean and focused solely on mathematical calculations.

🛤️ Architectural Evolution Journey

The codebase in this repository reflects the final assembled result of this step-by-step refactoring process, which evolved through the following stages:

  1. The Starting Point: A Primitive Implementation: The architecture begins with a basic indicator that draws a line based on a user-selected price. It formalizes logic for partial buffer recalculation by identifying three key scenarios: current bar changes, new bar appearances, or full recalculations. This initial procedural code serves as the foundation to be systematically refactored.
  2. Key Ingredients: Header Files and Object-Oriented Programming: The transition emphasizes using header files to organize large projects and make code truly reusable. Object-oriented programming is introduced to combat "spaghetti code" via encapsulation, bundling data and functions together. This approach eliminates the need for messy global variables and long function signatures.
  3. Encapsulating Logic: The First Module: The primitive applied price logic is refactored into a portable class (CAppliedPrice) and moved to its own header file. This transition cleans up the main indicator file, which now only manages a pointer to the class instance. By treating the applied price as a "sub-indicator" module, the code becomes much easier to maintain.
  4. Eliminating Global State: Introducing CIndicator: To prevent naming collisions and improve structural integrity, a root CIndicator class is introduced to house the entire program logic. The goal is to eliminate global state entirely, leaving nothing in the global scope except a single pointer to the main indicator object. This pact ensures all data and logic are safely encapsulated.
  5. Isolating Input Parameters: Managing numerous input parameters is simplified by wrapping them into a dedicated CIndiParams class. Inheritance with a base class allows the core logic to access necessary settings while remaining isolated from the global input variables. This design ensures that adding or removing inputs doesn't require tedious updates across multiple files.
  6. Extracting Common Traits: CSubIndiBase: A base class named CSubIndiBase is created to standardize common traits shared by all sub-indicators, such as minimum bar requirements. It introduces essential fields like drawBegin and emptyValue to consistently handle technical requirements for indicator plots. This abstraction allows different modules to communicate their needs through a unified interface.
  7. Centralizing Requirements: CSubIndiRegistry: A static CSubIndiRegistry class is introduced to centrally track the maximum bar requirements of all active sub-indicators. This eliminates repetitive boilerplate checks at the start of every module's calculation method. By having sub-indicators register themselves automatically, the system performs a single, efficient sanity check.
  8. Hiding Infrastructure Boilerplate: CIndicatorBase: To further clean up the workspace, a CIndicatorBase class is implemented to hide routine infrastructure logic that rarely changes. This base class handles the registry checks and initialization tasks, freeing the developer to focus on wiring modules together. This separation of concerns keeps the most frequently edited files concise.
  9. Building the Second Module: SMA: The framework is put to the test by building a Simple Moving Average (SMA) module that operates on top of the applied price module. This step explains how to calculate total bars required for chained indicators by combining the requirements of the source and the new module. It demonstrates the power of assembling complex tools from self-contained components.
  10. Calculating the Simple Moving Average: The mathematical implementation of the SMA is broken down into distinct methods for clearing buffers and applying recurrence formulas. By adapting standard MQL5 moving average logic to the new architecture, the module produces results identical to built-in indicators. This highlights how the framework handles the specific "math that matters".
  11. DRYing Sub-Indicators: Pulling Logic Up: In the final refactoring step, the "Don't Repeat Yourself" (DRY) principle is applied by moving common calculation preparation logic into the base class. A virtual hook called clearBuffersAt allows the base class to manage buffer clearing while subclasses define specific data structures. The result is a highly refined architecture stripped of redundancy.

📊 Statistics

  • Article length: extensive, multi-section tutorial
  • Code examples: 31 numbered code snippets
  • Architecture scope: primitive indicator -> scalable modular framework
  • Educational commits: 22 single-purpose commits tracing the full evolution
  • Incoming links: 40 direct links from the article pointing to this repository's Git history

📂 Repository Structure

evolution/
├── Evolution.mq5
└── src/
    ├── AppliedPrice.mqh
    ├── Indicator.mqh
    ├── IndicatorBase.mqh
    ├── IndiParamsBase.mqh
    ├── InputsAndRelated.mqh
    ├── Sma.mqh
    ├── SubIndiBase.mqh
    └── SubIndiRegistry.mqh

File Descriptions

  • Evolution.mq5: The main indicator file acting as the entry point and containing the MQL5 event handlers.
  • Indicator.mqh: The root object that wires all sub-indicators together, eliminating the need for global state.
  • IndicatorBase.mqh: Abstracts away infrastructure boilerplate (such as rates_total checks) from the main indicator logic.
  • SubIndiBase.mqh: The base class for all sub-indicators that extracts common traits and handles routine boilerplate.
  • SubIndiRegistry.mqh: A static registry that centrally manages the barsRequired requirement across all modules.
  • Sma.mqh: A sub-indicator that calculates the Simple Moving Average.
  • AppliedPrice.mqh: A sub-indicator that calculates the base applied price.
  • IndiParamsBase.mqh: Defines the base structure for indicator parameters, decoupling the core logic from global input variables.
  • InputsAndRelated.mqh: Defines the global input variables and the derived class mapping them to the parameter object.

📚 Reference

  • Vladislav Boyko, “Modular Indicator Architecture in MQL5 (Part 1): Stop Copy-Pasting and Start Writing Scalable, Reusable Code”, MQL5, article 17566, 31 May 2026 https://www.mql5.com/en/articles/17566

🏷️ Tags

mql5 indicator oop framework refactoring modular-architecture custom-indicator algoforge