13 KiB
MQL5 Conventions - TSN Ecosystem
Standards and best practices for writing MQL5 code in the TSN ecosystem
Table of Contents
Naming
Classes
- Prefix:
C - Type:
PascalCase - Examples:
CMyClass,CIndicator,COrderBlock,CRiskManagement
class CStrategy {
// implementation
};
Interfaces
- Prefix:
I - Type:
PascalCase - Examples:
IStrategy,INotifiable
interface IStrategy {
void OnTick();
};
Structures
- Prefix: None
- Type:
PascalCase - Examples:
Bar,OrderData,SignalInfo
struct OrderData {
ulong ticket;
double price;
datetime time;
};
Unions
- Prefix: None
- Type:
PascalCase
union DataValue {
double dval;
int ival;
};
Functions
- Prefix: None
- Type:
PascalCase - Examples:
CalculateSma(),GetSupportLevel(),ApplyRiskManagement()
double CalculateSma(const double &series[], int period) {
// implementation
}
Member Variables (Class)
- Prefix:
m_ - Type:
snake_case - Examples:
m_period,m_buffer,m_is_active
class CIndicator {
private:
int m_period;
double m_buffer[];
bool m_is_active;
};
Global Variables
- Prefix:
g_ - Type:
snake_case - Examples:
g_risk,g_order_block,g_bar_manager
CRiskManagement* g_risk;
COrderBlockTick g_ob;
CBarManager g_bar_manager;
Local Variables
- Prefix: None
- Type:
snake_case - Examples:
current_price,support_level,trade_signal
void OnTick() {
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
int period = 14;
bool is_valid = true;
}
Input Parameters
- Prefix:
Inp - Type:
PascalCase - Examples:
InpPeriod,InpStopLoss,InpTakeProfitRatio
input int InpPeriod = 14;
input double InpStopLoss = 50.0;
input string InpTelegramToken = "your_token";
Constants \ Defines \ Enums
- Type:
UPPER_SNAKE_CASE - Examples:
MAX_ITERATIONS,DEFAULT_PERIOD,VERSION_NUMBER
const int DEFAULT_PERIOD = 20;
const double MAX_SPREAD = 0.5;
#define VERSION_NUMBER 100
enum ENUM_RISK_TYPE
{
RISK_TYPE_PERCENTAGE,
RISK_TYPE_MONEY
};
Context Resolution
In case libraries like winapi or internal class/global functions conflict with native functions, it is recommended to use :: to indicate to the compiler that it should call the native function. For library functions like kernel32, for example GetLastError(), you can resolve the context as kernel32::GetLastError()
File Structure
Recommended Hierarchy for EAs
For simple or moderately complex EAs, it is recommended to divide them into the following files:
EA_Name/
├── Global.mqh (Global initialization - ALWAYS FIRST)
├── Defines.mqh (Includes and global variables)
├── Functions.mqh (Helper functions)
├── Main.mqh (Main logic/classes)
└── EA_Name.mq5 (Main event)
Include Order
Always include Global.mqh first to avoid "Invalid Pointer" errors in global instances:
// Defines.mqh
#include "Global.mqh" // ALWAYS FIRST
#include <TSN\\MQLArticles\\Strategy\\Main.mqh>
#include "Functions.mqh"
#include "Main.mqh"
// EA_Name.mq5
#include "Defines.mqh" // Already contains Global.mqh
Code Style
MQL5 IDE Configuration
Tools → Options → Stylizer:
| Option | Value |
|---|---|
| Style | MetaQuotes |
| Replace tabs with spaces | Yes |
| Remove empty lines | No |
| Insert spaces after comma/semicolon | Yes |
| Insert spaces around operators | Yes |
Tools → Options → General:
| Option | Value |
|---|---|
| Tab (characters) | 2 |
| Insert spaces | Yes |
| Insert () and {} ) ] " ' | Yes |
| Highlight paired parentheses | Yes |
| Highlight current line | Yes |
| Enable clipboard history | Yes |
| Auto name list | Yes |
| Auto indent | Yes |
| Auto parameters | Yes |
| Line numbers | Yes |
Keyboard Shortcuts
- Format code:
CTRL + ,, use it always when you finish coding something - Insert function/class/general section/include header:
CTRL + ., use before defining a class or defines\enums\structs, functions:
//+------------------------------------------------------------------+
//| Include |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Defines |
//+------------------------------------------------------------------+
#define MAX_POINTS (10)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CRect
{
public:
CRect() {}
~CRect() {}
void Draw();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRect::Draw
{
// Logic....
}
Section Separation and Internal Comments
Use //--- to divide logical sections within code blocks:
void OnInit() {
//--- Initialization
g_risk = new CRiskManagement();
g_ob.init(Symbol(), PERIOD_H1);
//---
return INIT_SUCCEEDED;
}
}
Use // to comment certain parts. Also, if the text is no longer visible in a single line and requires HSCROLL, then place it above:
void OnInit() {
//--- Initialization
g_risk = new CRiskManagement(); // Dynamic instance of CRiskManagement (Short text here)
// ............................................................ Very long text above (approx covers 60-70%)
g_ob.init(Symbol(), PERIOD_H1);
//---
return INIT_SUCCEEDED;
}
}
Complete Visual Separation
For important sections, use the complete header:
//+------------------------------------------------------------------+
//| Global variables |
//+------------------------------------------------------------------+
CRiskManagement* g_risk;
COrderBlockTick g_ob;
//+------------------------------------------------------------------+
//| Main Signal Processing Function |
//+------------------------------------------------------------------+
void ProcessSignals() {
// logic
}
EA Structure
Recommended Flow
- Global.mqh - Initial configuration (BarManager, base libraries)
- Defines.mqh - Includes and global variables
- Functions.mqh - Helper functions
- Main.mqh - Strategy logic (classes)
- EA.mq5 - EA events
Example: Global.mqh
//+------------------------------------------------------------------+
//| Global.mqh |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, nique_372"
#property link "https://www.mql5.com/es/users/nique_372"
#property strict
// Base libraries that require immediate initialization
#include <TSN\\TimeUtils\\TimeUtils.mqh>
#include <TSN\\ICTLibrary\\Core.mqh>
//---
// Configure Bar Manager (critical to avoid Invalid Pointer)
CBarManager* p_bar_manager;
void InitGlobal() {
p_bar_manager = new CBarManager(_Symbol, _Period);
if (p_bar_manager == NULL) {
Print("Error: Could not initialize Bar Manager");
}
}
Example: Defines.mqh
//+------------------------------------------------------------------+
//| Defines.mqh |
//+------------------------------------------------------------------+
#include "Global.mqh" // FIRST
// Extra dependencies, although global should generally include what's necessary
#include <TSN\\MQLArticles\\Strategy\\Main.mqh>
//---
// Global variables
CRiskManagement* g_risk;
COrderBlockTick g_ob;
CStrategy* g_strategy;
//---
// Parameters
input int InpPeriod = 20;
input double InpRiskPercentage = 2.0;
input bool InpUseTelegram = true;
Example: Main.mqh
//+------------------------------------------------------------------+
//| Main.mqh |
//+------------------------------------------------------------------+
#include "Defines.mqh" // or Functions.mqh if applicable
class CMyStrategy : public CStrategy {
private:
int m_period;
double m_rsi_threshold;
public:
CMyStrategy(int InpPeriod, double InpThreshold) :
m_period(InpPeriod),
m_rsi_threshold(InpThreshold) {
}
void OnTick() override {
//---
// Logic
}
};
Example: EA.mq5
//+------------------------------------------------------------------+
//| MyTradingEA.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, nique_372"
#include "Main.mqh" // Contains Global.mqh
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
InitGlobal();
g_risk = new CRiskManagement(InpRiskPercentage);
g_strategy = new CMyStrategy(InpPeriod, 30.0);
if (g_risk == NULL || g_strategy == NULL) {
Print("Error in initialization");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
g_strategy.OnTick();
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
delete g_strategy;
delete g_risk;
}
Comments and Documentation
Function Headers
In the TSN ecosystem, the documentation method varies depending on complexity and whether it's a product:
- If it's a product, it's generally documented in the repository wiki (this also applies to free repositories)
- If there's no wiki, it's generally documented in the code itself
//+------------------------------------------------------------------+
//| Description.... |
//| Inputs: |
//| - series: double array where the series is located |
//| - period: calculation period |
//| Output: |
//| - Sma |
//| Notes: |
//| - Period must be greater than 0 |
//+------------------------------------------------------------------+
double CalculateSma(const double &series[], int period) {
double sum = 0.0;
for (int i = 0; i < period; i++) {
sum += series[i];
}
return sum / period;
}
And if it's on the wiki, a similar style to the MQL5 Docs is followed - one page per function... and what it receives, etc.
File Header mqh (in case of EA, additional properties such as description, icon, etc. can be added)
Always include at the beginning:
//+------------------------------------------------------------------+
//| <FILE_NAME>|
//| Copyright <YEAR>, <YOUR NAME>|
//| <URL> |
//+------------------------------------------------------------------+
#property copyright "Copyright <YEAR>, <YOUR NAME>"
#property link "<URL>"
#property strict
#ifndef REPONAME_PATHTOFILE
#define REPONAME_PATHTOFILE
#endif REPONAME_PATHTOFILE
Placeholder Descriptions
- FILE_NAME: Name of the mqh file
- YEAR: Current year
- YOUR NAME: Name of the code writer\org
- URL: Creator's URL, e.g.: [Website, MQL5 Profile, Personal Website, Github, etc..]
All these fields are defined before creating the file, some when creating the project, others in the file itself.
Language
Current Recommendation
Spanish is recommended for code internal to the TSN ecosystem, but with considerations:
- Public variables/libraries: Consider English for greater reach (this depends on your knowledge of that language)
- Complex comments: Spanish for clarity
- Class/function names: Consistency in the project
Historical Note
Important: Some repositories in the TSN ecosystem (especially the older ones) do not exactly follow these conventions. This is because they were developed before formal standards were implemented.
- Old code: ~50-70% adherence to conventions
- Recent code: ~90%+ adherence
There is a progressive migration to these conventions.
Questions or suggestions? Open an issue or contact @nique_372