TSNReposIndex/Conventions/MQL5.md

512 lines
No EOL
13 KiB
Markdown

# MQL5 Conventions - TSN Ecosystem
> Standards and best practices for writing MQL5 code in the TSN ecosystem
---
## Table of Contents
1. [Naming](#naming)
2. [File Structure](#file-structure)
3. [Code Style](#code-style)
4. [EA Structure](#ea-structure)
5. [Comments and Documentation](#comments-and-documentation)
6. [Language](#language)
---
## Naming
### Classes
- **Prefix:** `C`
- **Type:** `PascalCase`
- **Examples:** `CMyClass`, `CIndicator`, `COrderBlock`, `CRiskManagement`
```mql5
class CStrategy {
// implementation
};
```
### Interfaces
- **Prefix:** `I`
- **Type:** `PascalCase`
- **Examples:** `IStrategy`, `INotifiable`
```mql5
interface IStrategy {
void OnTick();
};
```
### Structures
- **Prefix:** None
- **Type:** `PascalCase`
- **Examples:** `Bar`, `OrderData`, `SignalInfo`
```mql5
struct OrderData {
ulong ticket;
double price;
datetime time;
};
```
### Unions
- **Prefix:** None
- **Type:** `PascalCase`
```mql5
union DataValue {
double dval;
int ival;
};
```
### Functions
- **Prefix:** None
- **Type:** `PascalCase`
- **Examples:** `CalculateSma()`, `GetSupportLevel()`, `ApplyRiskManagement()`
```mql5
double CalculateSma(const double &series[], int period) {
// implementation
}
```
### Member Variables (Class)
- **Prefix:** `m_`
- **Type:** `snake_case`
- **Examples:** `m_period`, `m_buffer`, `m_is_active`
```mql5
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`
```mql5
CRiskManagement* g_risk;
COrderBlockTick g_ob;
CBarManager g_bar_manager;
```
### Local Variables
- **Prefix:** None
- **Type:** `snake_case`
- **Examples:** `current_price`, `support_level`, `trade_signal`
```mql5
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`
```mql5
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`
```mql5
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:
```mql5
// Defines.mqh
#include "Global.mqh" // ALWAYS FIRST
#include <TSN\\MQLArticles\\Strategy\\Main.mqh>
#include "Functions.mqh"
#include "Main.mqh"
```
```mql5
// 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:
```mql5
//+------------------------------------------------------------------+
//| 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:
```mql5
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:
```mql5
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:
```mql5
//+------------------------------------------------------------------+
//| Global variables |
//+------------------------------------------------------------------+
CRiskManagement* g_risk;
COrderBlockTick g_ob;
//+------------------------------------------------------------------+
//| Main Signal Processing Function |
//+------------------------------------------------------------------+
void ProcessSignals() {
// logic
}
```
## EA Structure
### Recommended Flow
1. **Global.mqh** - Initial configuration (BarManager, base libraries)
2. **Defines.mqh** - Includes and global variables
3. **Functions.mqh** - Helper functions
4. **Main.mqh** - Strategy logic (classes)
5. **EA.mq5** - EA events
### Example: Global.mqh
```mql5
//+------------------------------------------------------------------+
//| 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
```mql5
//+------------------------------------------------------------------+
//| 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
```mql5
//+------------------------------------------------------------------+
//| 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
```mql5
//+------------------------------------------------------------------+
//| 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:
1. If it's a product, it's generally documented in the repository wiki (this also applies to free repositories)
2. If there's no wiki, it's generally documented in the code itself
```mql5
//+------------------------------------------------------------------+
//| 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:
```mql5
//+------------------------------------------------------------------+
//| <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](https://www.mql5.com/es/users/nique_372)