Files
opencode-skill/skills/mql-developer/references/mql4-reference.md
Kunthawat Greethong b26c8199a5 Update skills: add website-creator, mql-developer, ecommerce-astro
Changes:
- Add FAL_KEY and GEMINI_API_KEY to .env.example
- Update picture-it to use ~/.config/opencode/.env (unified creds)
- Remove shodh-memory skill (no longer used)
- Remove alphaear-* skills (deprecated)
- Remove thai-frontend-dev skill (replaced by website-creator)
- Remove theme-factory skill
- Add mql-developer skill (MQL5 trading)
- Add ecommerce-astro skill (Astro e-commerce)
- Add website-creator skill (Next.js + Payload CMS)
- Update install script for new skills
2026-04-16 17:40:27 +07:00

2052 lines
63 KiB
Markdown

# MQL4 Language Reference
Complete reference for MetaQuotes Language 4 (MQL4), used to develop Expert Advisors, custom indicators, scripts, and libraries for the MetaTrader 4 trading platform.
## Table of Contents
- [Data Types](#data-types)
- [Variables](#variables)
- [Operators](#operators)
- [Arrays](#arrays)
- [Strings](#strings)
- [Program Types](#program-types)
- [Predefined Variables](#predefined-variables)
- [Technical Indicator Functions](#technical-indicator-functions)
- [Order Management](#order-management)
- [Market Information](#market-information)
- [Account Functions](#account-functions)
- [Preprocessor Directives](#preprocessor-directives)
- [Error Handling](#error-handling)
- [Common Gotchas](#common-gotchas)
- [File Operations](#file-operations)
- [WebRequest](#webrequest)
- [Utility Functions](#utility-functions)
- [Global Terminal Variables](#global-terminal-variables)
---
## Data Types
### Integer Types
| Type | Size | Range |
|------|------|-------|
| `char` | 1 byte | -128 to 127 |
| `uchar` | 1 byte | 0 to 255 |
| `short` | 2 bytes | -32,768 to 32,767 |
| `ushort` | 2 bytes | 0 to 65,535 |
| `int` | 4 bytes | -2,147,483,648 to 2,147,483,647 |
| `uint` | 4 bytes | 0 to 4,294,967,295 |
| `long` | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
| `ulong` | 8 bytes | 0 to 18,446,744,073,709,551,615 |
### Floating-Point Types
| Type | Size | Significant Digits | Range |
|------|------|---------------------|-------|
| `float` | 4 bytes | 7 | 1.175e-38 to 3.402e+38 |
| `double` | 8 bytes | 15-16 | 2.225e-308 to 1.797e+308 |
### Other Types
| Type | Description | Example |
|------|-------------|---------|
| `bool` | Boolean | `true`, `false` |
| `string` | Character string | `"Hello World"` |
| `datetime` | Date and time (seconds since 1970.01.01) | `D'2024.01.15'`, `D'2024.01.15 10:30:00'` |
| `color` | RGB color | `clrRed`, `clrBlue`, `C'128,128,128'`, `0x00FF00` |
```mql4
// Type examples
int count = 10;
double price = 1.23456;
bool isActive = true;
string name = "EURUSD";
datetime startTime = D'2024.01.15 08:00:00';
color arrowColor = clrRed;
color custom = C'128,128,128';
```
### Enumerations
```mql4
enum ENUM_SIGNAL
{
SIGNAL_NONE = 0, // No signal
SIGNAL_BUY = 1, // Buy signal
SIGNAL_SELL = -1 // Sell signal
};
ENUM_SIGNAL signal = SIGNAL_BUY;
```
### Structures
```mql4
struct TradeSetup
{
string symbol;
int direction;
double entryPrice;
double stopLoss;
double takeProfit;
double lotSize;
};
TradeSetup setup;
setup.symbol = "EURUSD";
setup.direction = OP_BUY;
setup.entryPrice = Ask;
setup.stopLoss = Ask - 50 * Point;
setup.takeProfit = Ask + 100 * Point;
setup.lotSize = 0.1;
```
### Type Casting
```mql4
double price = 1.23456;
int pips = (int)(price * 10000); // Explicit cast
string text = DoubleToString(price, 5); // Function conversion
```
---
## Variables
### Local Variables
Declared inside a function. Exist only during function execution.
```mql4
void CalculateSignal()
{
int period = 14; // Local variable
double value = iRSI(NULL, 0, period, PRICE_CLOSE, 0);
}
```
### Global Variables
Declared outside any function. Accessible from all functions in the file. Initialized once when the program loads.
```mql4
double g_lotSize = 0.1; // Global variable (file scope)
int g_magicNumber = 12345;
int OnInit()
{
g_lotSize = 0.2; // Accessible here
return INIT_SUCCEEDED;
}
void OnTick()
{
double lots = g_lotSize; // Accessible here too
}
```
### Static Variables
Retain their value between function calls. Initialized only once.
```mql4
void OnTick()
{
static int tickCount = 0; // Initialized once
static datetime lastBarTime = 0;
tickCount++;
if(Time[0] != lastBarTime)
{
lastBarTime = Time[0];
// New bar logic here
}
}
```
### Input Variables
User-configurable parameters. Appear in the EA/indicator properties dialog. Read-only at runtime.
```mql4
input int InpMAPeriod = 14; // MA Period
input double InpLotSize = 0.1; // Lot Size
input string InpComment = "MyEA"; // Order Comment
input bool InpUseFilter = true; // Use Trend Filter
input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // MA Method
```
### Extern Variables
Similar to `input` but can be modified at runtime. Legacy; prefer `input`.
```mql4
extern int ExtPeriod = 20; // Period
extern double ExtFactor = 1.5; // Factor
void OnTick()
{
ExtPeriod = 30; // Allowed (unlike input variables)
}
```
---
## Operators
### Arithmetic Operators
| Operator | Description | Example |
|----------|-------------|---------|
| `+` | Addition | `a + b` |
| `-` | Subtraction | `a - b` |
| `*` | Multiplication | `a * b` |
| `/` | Division | `a / b` |
| `%` | Modulo | `a % b` |
| `++` | Increment | `a++` or `++a` |
| `--` | Decrement | `a--` or `--a` |
### Assignment Operators
| Operator | Description | Equivalent |
|----------|-------------|------------|
| `=` | Assign | `a = b` |
| `+=` | Add and assign | `a = a + b` |
| `-=` | Subtract and assign | `a = a - b` |
| `*=` | Multiply and assign | `a = a * b` |
| `/=` | Divide and assign | `a = a / b` |
| `%=` | Modulo and assign | `a = a % b` |
### Comparison Operators
| Operator | Description |
|----------|-------------|
| `==` | Equal to |
| `!=` | Not equal to |
| `<` | Less than |
| `>` | Greater than |
| `<=` | Less than or equal to |
| `>=` | Greater than or equal to |
### Logical Operators
| Operator | Description | Example |
|----------|-------------|---------|
| `&&` | Logical AND | `a > 0 && b > 0` |
| `\|\|` | Logical OR | `a > 0 \|\| b > 0` |
| `!` | Logical NOT | `!isActive` |
### Bitwise Operators
| Operator | Description |
|----------|-------------|
| `&` | Bitwise AND |
| `\|` | Bitwise OR |
| `^` | Bitwise XOR |
| `~` | Bitwise NOT |
| `<<` | Left shift |
| `>>` | Right shift |
### Ternary Operator
```mql4
double lots = (AccountBalance() > 10000) ? 0.2 : 0.1;
string direction = (signal == SIGNAL_BUY) ? "BUY" : "SELL";
```
---
## Arrays
### Static Arrays
Fixed size, determined at compile time.
```mql4
double prices[100];
int values[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
string symbols[3] = {"EURUSD", "GBPUSD", "USDJPY"};
```
### Dynamic Arrays
Size determined at runtime. Must be resized with `ArrayResize()`.
```mql4
double buffer[];
ArrayResize(buffer, 100);
buffer[0] = 1.23456;
```
### Multi-Dimensional Arrays
```mql4
double matrix[3][4]; // Static 3x4 matrix
double grid[][5]; // Dynamic first dimension, fixed second
ArrayResize(grid, 10); // Now 10x5
```
### Array Functions
```mql4
// int ArrayResize(array&, int new_size, int reserve_size=0)
// Resizes a dynamic array. Returns new size or -1 on failure.
double data[];
int newSize = ArrayResize(data, 200);
// int ArraySize(const array&)
// Returns the total number of elements.
int count = ArraySize(data); // 200
// int ArrayCopy(dst&, const src, int dst_start=0, int src_start=0, int count=WHOLE_ARRAY)
// Copies elements from one array to another.
double source[5] = {1.0, 2.0, 3.0, 4.0, 5.0};
double dest[];
ArrayResize(dest, 5);
ArrayCopy(dest, source);
// bool ArraySort(array&, int count=WHOLE_ARRAY, int start=0, int direction=MODE_ASCEND)
// Sorts a numeric array. direction: MODE_ASCEND or MODE_DESCEND.
double values[5] = {3.0, 1.0, 4.0, 1.5, 2.0};
ArraySort(values); // {1.0, 1.5, 2.0, 3.0, 4.0}
// void ArrayInitialize(array&, double value)
// Fills all elements with the given value.
double arr[100];
ArrayInitialize(arr, 0.0);
// int ArrayMaximum(const array&, int count=WHOLE_ARRAY, int start=0)
// Returns the index of the maximum value.
int maxIdx = ArrayMaximum(values);
// int ArrayMinimum(const array&, int count=WHOLE_ARRAY, int start=0)
// Returns the index of the minimum value.
int minIdx = ArrayMinimum(values);
// void ArrayFree(array&)
// Frees memory of a dynamic array and sets size to 0.
ArrayFree(data);
// bool ArraySetAsSeries(array&, bool flag)
// Sets reverse indexing (index 0 = last element). Used for indicator buffers.
double buffer[];
ArraySetAsSeries(buffer, true); // Now buffer[0] is the newest element
```
---
## Strings
### String Functions
```mql4
// int StringLen(string value)
int len = StringLen("Hello"); // 5
// string StringSubstr(string value, int start, int length=-1)
string sub = StringSubstr("EURUSD", 0, 3); // "EUR"
// int StringFind(string value, string match, int start=0)
// Returns position or -1 if not found.
int pos = StringFind("EURUSD", "USD"); // 3
// int StringReplace(string& str, string find, string replacement)
// Returns number of replacements made.
string text = "Hello World";
int count = StringReplace(text, "World", "MQL4"); // text = "Hello MQL4"
// string StringConcatenate(...)
// Concatenates multiple values. Faster than + operator for many strings.
string result = StringConcatenate("Price: ", DoubleToString(Ask, 5), " Time: ", TimeToString(TimeCurrent()));
// string StringToUpper(string value)
string upper = "eurusd";
StringToUpper(upper); // upper = "EURUSD"
// Note: StringToLower() also available
// int StringToInteger(string value)
int num = StringToInteger("42"); // 42
// double StringToDouble(string value)
double val = StringToDouble("1.23456"); // 1.23456
// string IntegerToString(long value, int str_len=0, ushort fill=' ')
string s1 = IntegerToString(42); // "42"
string s2 = IntegerToString(5, 3, '0'); // "005"
// string DoubleToString(double value, int digits=8)
string s3 = DoubleToString(1.23456, 5); // "1.23456"
string s4 = DoubleToString(1.23456, 2); // "1.23"
// Additional useful conversions
// string TimeToString(datetime value, int mode=TIME_DATE|TIME_MINUTES)
// datetime StringToTime(string value)
// string EnumToString(enum_value)
```
### String Formatting with StringFormat
```mql4
// Works like C printf
string msg = StringFormat("Symbol: %s | Price: %.5f | Spread: %d",
_Symbol, Ask, (int)MarketInfo(_Symbol, MODE_SPREAD));
// "Symbol: EURUSD | Price: 1.23456 | Spread: 12"
```
---
## Program Types
### Expert Advisors (EAs)
Automated trading programs that run on charts and can place/modify/close orders.
```mql4
//+------------------------------------------------------------------+
//| MyExpert.mq4 |
//| Copyright 2024, Author |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Author"
#property link "https://www.example.com"
#property version "1.00"
#property strict
//--- Input parameters
input int InpMAPeriod = 14; // MA Period
input double InpLotSize = 0.1; // Lot Size
input int InpStopLoss = 50; // Stop Loss (points)
input int InpTakeProfit = 100; // Take Profit (points)
input int InpMagicNumber = 12345; // Magic Number
input int InpSlippage = 3; // Slippage (points)
//--- Global variables
double g_pipSize;
int g_pipDigits;
//+------------------------------------------------------------------+
//| Expert initialization function |
//| Called once when the EA is loaded onto a chart. |
//| Return: INIT_SUCCEEDED, INIT_FAILED, |
//| INIT_PARAMETERS_INCORRECT, INIT_AGENT_NOT_SUITABLE |
//+------------------------------------------------------------------+
int OnInit()
{
// Detect broker digit mode
if(Digits == 5 || Digits == 3)
{
g_pipSize = Point * 10;
g_pipDigits = 1;
}
else
{
g_pipSize = Point;
g_pipDigits = 0;
}
// Set up timer (optional)
EventSetTimer(60); // Fire OnTimer() every 60 seconds
Print("EA initialized on ", _Symbol, " | PipSize=", g_pipSize);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//| Called when the EA is removed or the terminal is closed. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
string reasonText;
switch(reason)
{
case REASON_REMOVE: reasonText = "Removed from chart"; break;
case REASON_RECOMPILE: reasonText = "Recompiled"; break;
case REASON_CHARTCHANGE: reasonText = "Symbol or period changed"; break;
case REASON_CHARTCLOSE: reasonText = "Chart closed"; break;
case REASON_PARAMETERS: reasonText = "Inputs changed"; break;
case REASON_ACCOUNT: reasonText = "Account changed"; break;
case REASON_TEMPLATE: reasonText = "Template applied"; break;
case REASON_INITFAILED: reasonText = "OnInit failed"; break;
case REASON_CLOSE: reasonText = "Terminal closed"; break;
default: reasonText = "Unknown"; break;
}
Print("EA deinitialized. Reason: ", reasonText);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//| Called on every new tick (price change) for the chart symbol. |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if trading is allowed
if(!IsTradeAllowed()) return;
if(!IsConnected()) return;
// New bar detection
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];
// Trading logic here
double maValue = iMA(NULL, 0, InpMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
if(Close[1] > maValue && CountOrders(OP_BUY) == 0)
{
OpenOrder(OP_BUY);
}
else if(Close[1] < maValue && CountOrders(OP_SELL) == 0)
{
OpenOrder(OP_SELL);
}
}
//+------------------------------------------------------------------+
//| Timer function |
//| Called at the interval set by EventSetTimer(). |
//+------------------------------------------------------------------+
void OnTimer()
{
// Periodic tasks: cleanup, status updates, etc.
Print("Timer event at ", TimeToString(TimeCurrent()));
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//| Called when chart events occur (clicks, key presses, objects). |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id == CHARTEVENT_CLICK)
{
Print("Chart clicked at x=", lparam, " y=", dparam);
}
else if(id == CHARTEVENT_KEYDOWN)
{
Print("Key pressed: ", lparam);
}
else if(id == CHARTEVENT_OBJECT_CLICK)
{
Print("Object clicked: ", sparam);
}
}
//+------------------------------------------------------------------+
//| Count open orders by type for this EA |
//+------------------------------------------------------------------+
int CountOrders(int orderType)
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != _Symbol) continue;
if(OrderMagicNumber() != InpMagicNumber) continue;
if(OrderType() == orderType) count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Open a market order |
//+------------------------------------------------------------------+
bool OpenOrder(int orderType)
{
double price, sl, tp;
RefreshRates();
if(orderType == OP_BUY)
{
price = Ask;
sl = (InpStopLoss > 0) ? NormalizeDouble(Ask - InpStopLoss * Point, Digits) : 0;
tp = (InpTakeProfit > 0) ? NormalizeDouble(Ask + InpTakeProfit * Point, Digits) : 0;
}
else
{
price = Bid;
sl = (InpStopLoss > 0) ? NormalizeDouble(Bid + InpStopLoss * Point, Digits) : 0;
tp = (InpTakeProfit > 0) ? NormalizeDouble(Bid - InpTakeProfit * Point, Digits) : 0;
}
int ticket = OrderSend(_Symbol, orderType, InpLotSize, price, InpSlippage,
sl, tp, "MyEA", InpMagicNumber, 0, clrGreen);
if(ticket < 0)
{
Print("OrderSend failed. Error: ", GetLastError());
return false;
}
Print("Order opened. Ticket: ", ticket);
return true;
}
```
#### Deinit Reason Constants
| Constant | Value | Description |
|----------|-------|-------------|
| `REASON_PROGRAM` | 0 | EA called `ExpertRemove()` |
| `REASON_REMOVE` | 1 | Removed from chart |
| `REASON_RECOMPILE` | 2 | Recompiled |
| `REASON_CHARTCHANGE` | 3 | Symbol or period changed |
| `REASON_CHARTCLOSE` | 4 | Chart closed |
| `REASON_PARAMETERS` | 5 | Input parameters changed |
| `REASON_ACCOUNT` | 6 | Account changed |
| `REASON_TEMPLATE` | 7 | New template applied |
| `REASON_INITFAILED` | 8 | `OnInit()` returned non-zero |
| `REASON_CLOSE` | 9 | Terminal closed |
---
### Custom Indicators
Calculate and display values on charts. Cannot trade. Run in the interface thread (must be fast).
#### Property Directives for Indicators
```mql4
#property indicator_chart_window // Draw on main chart
// OR
#property indicator_separate_window // Draw in separate sub-window
#property indicator_buffers 2 // Number of visible plot buffers
#property indicator_color1 clrDodgerBlue // Color for buffer 0
#property indicator_color2 clrRed // Color for buffer 1
#property indicator_width1 2 // Line width for buffer 0
#property indicator_width2 1 // Line width for buffer 1
#property indicator_style1 STYLE_SOLID // Line style for buffer 0
#property indicator_style2 STYLE_DOT // Line style for buffer 1
// For separate window indicators
#property indicator_minimum 0 // Minimum scale
#property indicator_maximum 100 // Maximum scale
#property indicator_level1 30 // Horizontal level line
#property indicator_level2 70 // Another level line
#property indicator_levelcolor clrGray // Level line color
#property indicator_levelstyle STYLE_DOT // Level line style
```
**Important:** `#property indicator_buffers N` sets the number of **plotted** (visible) buffers. If you need additional calculation buffers that are not drawn, use `IndicatorBuffers(total)` in `OnInit()` to set the **total** number of buffers (plotted + calculation). The total must be >= the `#property indicator_buffers` value.
```mql4
#property indicator_buffers 2 // 2 visible plots
double PlotBuffer1[]; // Visible buffer 0
double PlotBuffer2[]; // Visible buffer 1
double CalcBuffer[]; // Hidden calculation buffer
int OnInit()
{
IndicatorBuffers(3); // Total = 2 visible + 1 calculation
SetIndexBuffer(0, PlotBuffer1);
SetIndexBuffer(1, PlotBuffer2);
SetIndexBuffer(2, CalcBuffer); // Not plotted (index >= indicator_buffers)
return INIT_SUCCEEDED;
}
```
#### Draw Styles
| Constant | Description |
|----------|-------------|
| `DRAW_LINE` | Simple line connecting buffer values |
| `DRAW_HISTOGRAM` | Vertical bars from zero line to buffer value |
| `DRAW_ARROW` | Symbols/arrows at buffer values |
| `DRAW_NONE` | Not drawn (for calculation buffers or when using `IndicatorBuffers`) |
| `DRAW_SECTION` | Line segments between non-empty values |
| `DRAW_ZIGZAG` | Zigzag line (requires two buffers) |
#### Full Indicator Template
```mql4
//+------------------------------------------------------------------+
//| MyIndicator.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Author"
#property link "https://www.example.com"
#property version "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrRed
#property indicator_width1 2
#property indicator_width2 1
#property indicator_level1 30
#property indicator_level2 70
#property indicator_minimum 0
#property indicator_maximum 100
//--- Input parameters
input int InpPeriod = 14; // Period
//--- Indicator buffers
double MainBuffer[];
double SignalBuffer[];
//+------------------------------------------------------------------+
int OnInit()
{
// Bind arrays to indicator buffers
SetIndexBuffer(0, MainBuffer);
SetIndexBuffer(1, SignalBuffer);
// Set drawing styles
SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2);
SetIndexStyle(1, DRAW_LINE, STYLE_DOT, 1);
// Set labels (shown in Data Window)
SetIndexLabel(0, "Main");
SetIndexLabel(1, "Signal");
// Set indicator name in sub-window
IndicatorShortName("MyIndicator(" + IntegerToString(InpPeriod) + ")");
// Set how many initial bars to skip
SetIndexDrawBegin(0, InpPeriod);
SetIndexDrawBegin(1, InpPeriod);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| OnCalculate - called on every tick and on history load |
//| |
//| rates_total - total number of bars available |
//| prev_calculated - bars calculated on previous call (0 on first) |
//| time[] - bar open times |
//| open[] - bar open prices |
//| high[] - bar high prices |
//| low[] - bar low prices |
//| close[] - bar close prices |
//| tick_volume[] - tick volumes |
//| volume[] - real volumes |
//| spread[] - spreads |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Insufficient bars
if(rates_total < InpPeriod) return 0;
// Determine starting index
int start;
if(prev_calculated == 0)
start = InpPeriod; // First calculation
else
start = prev_calculated - 1; // Recalculate only new bars
// Main calculation loop (oldest to newest)
for(int i = start; i < rates_total; i++)
{
// Example: simple calculation
double sum = 0;
for(int j = 0; j < InpPeriod; j++)
sum += close[i - j];
MainBuffer[i] = sum / InpPeriod;
SignalBuffer[i] = MainBuffer[i] * 0.95;
}
return rates_total; // Return for next prev_calculated
}
```
#### SetIndexBuffer / SetIndexStyle / SetIndexLabel
```mql4
// bool SetIndexBuffer(int index, double array[])
// Binds an array to an indicator buffer at the given index.
SetIndexBuffer(0, MainBuffer);
// void SetIndexStyle(int index, int type, int style=EMPTY, int width=EMPTY, color clr=CLR_NONE)
// type: DRAW_LINE, DRAW_HISTOGRAM, DRAW_ARROW, DRAW_NONE, etc.
// style: STYLE_SOLID, STYLE_DASH, STYLE_DOT, STYLE_DASHDOT, STYLE_DASHDOTDOT
SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrBlue);
// void SetIndexLabel(int index, string text)
// Sets the label shown in the Data Window. Use NULL to hide.
SetIndexLabel(0, "Fast MA");
SetIndexLabel(1, NULL); // Hidden from Data Window
// void SetIndexArrow(int index, int code)
// Sets the Wingdings symbol code for DRAW_ARROW style.
SetIndexStyle(2, DRAW_ARROW);
SetIndexArrow(2, 233); // Up arrow
SetIndexArrow(3, 234); // Down arrow
// void SetIndexDrawBegin(int index, int begin)
// Sets the bar number from which drawing starts.
SetIndexDrawBegin(0, InpPeriod);
// void SetIndexEmptyValue(int index, double value)
// Sets the value considered "empty" (not drawn). Default is EMPTY_VALUE.
SetIndexEmptyValue(0, 0.0);
```
---
### Scripts
Execute once and terminate. Can trade. Useful for one-time operations.
```mql4
//+------------------------------------------------------------------+
//| CloseAll.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Author"
#property link "https://www.example.com"
#property version "1.00"
#property strict
#property show_inputs // Show input dialog before running
input int InpMagicNumber = 0; // Magic Number (0 = all)
//+------------------------------------------------------------------+
//| Script start function - called once when script is launched |
//+------------------------------------------------------------------+
void OnStart()
{
int closed = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != _Symbol) continue;
if(InpMagicNumber != 0 && OrderMagicNumber() != InpMagicNumber) continue;
bool result = false;
RefreshRates();
if(OrderType() == OP_BUY)
result = OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrRed);
else if(OrderType() == OP_SELL)
result = OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrRed);
else
result = OrderDelete(OrderTicket());
if(result) closed++;
else Print("Failed to close order #", OrderTicket(), " Error: ", GetLastError());
}
Print("Closed ", closed, " orders");
}
```
---
### Libraries
Reusable function collections. Cannot run independently.
```mql4
//+------------------------------------------------------------------+
//| TradeLib.mq4 |
//+------------------------------------------------------------------+
#property library
#property copyright "Copyright 2024, Author"
#property strict
//--- Exported function (callable from other programs)
double CalculateLotSize(double riskPercent, double stopLossPips) export
{
double accountRisk = AccountBalance() * riskPercent / 100.0;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(tickValue == 0 || stopLossPips == 0) return minLot;
double lots = accountRisk / (stopLossPips * (tickValue / tickSize));
lots = MathFloor(lots / lotStep) * lotStep;
lots = MathMax(minLot, MathMin(maxLot, lots));
return NormalizeDouble(lots, 2);
}
```
#### Importing a Library
```mql4
// Import from compiled .ex4 library
#import "TradeLib.ex4"
double CalculateLotSize(double riskPercent, double stopLossPips);
#import
// Import from Windows DLL
#import "user32.dll"
int MessageBoxW(int hWnd, string text, string caption, int type);
#import
// Usage
void OnTick()
{
double lots = CalculateLotSize(2.0, 50);
}
```
---
## Predefined Variables
These are built-in variables updated by the terminal. Available in all program types.
| Variable | Type | Description |
|----------|------|-------------|
| `Ask` | `double` | Current ask price (buy price) |
| `Bid` | `double` | Current bid price (sell price) |
| `Bars` | `int` | Number of bars on the current chart |
| `Point` | `double` | Point size (0.00001 for 5-digit, 0.0001 for 4-digit) |
| `Digits` | `int` | Number of decimal places (5 or 3 for 5-digit brokers) |
| `_Symbol` | `string` | Current chart symbol (same as `Symbol()`) |
| `_Period` | `int` | Current chart timeframe in minutes (same as `Period()`) |
| `Open[]` | `double` | Array of bar open prices |
| `High[]` | `double` | Array of bar high prices |
| `Low[]` | `double` | Array of bar low prices |
| `Close[]` | `double` | Array of bar close prices |
| `Time[]` | `datetime` | Array of bar open times |
| `Volume[]` | `long` | Array of bar tick volumes |
**Important:** The series arrays (`Open[]`, `High[]`, `Low[]`, `Close[]`, `Time[]`, `Volume[]`) use reverse indexing: **index 0 = the newest (current) bar**. Index 1 = previous bar, etc.
```mql4
double currentClose = Close[0]; // Current bar's close
double previousHigh = High[1]; // Previous bar's high
datetime barTime = Time[0]; // Current bar's open time
double currentSpread = Ask - Bid; // Current spread
// IMPORTANT: Call RefreshRates() before using Ask/Bid in order functions
// to ensure you have the latest prices.
RefreshRates();
double freshAsk = Ask;
double freshBid = Bid;
```
---
## Technical Indicator Functions
All built-in indicator functions follow the pattern:
`iFunction(symbol, timeframe, ...parameters, shift)`
- `symbol`: Use `NULL` or `_Symbol` for the current chart symbol.
- `timeframe`: Use `0` or `PERIOD_CURRENT` for the current chart timeframe.
- `shift`: Bar index (0 = current bar, 1 = previous bar, etc.).
### Moving Average - iMA
```mql4
double iMA(
string symbol, // Symbol name
int timeframe, // Timeframe (PERIOD_*)
int period, // Averaging period
int ma_shift, // Horizontal shift (bars)
int ma_method, // MA method (MODE_SMA, etc.)
int applied_price, // Applied price (PRICE_CLOSE, etc.)
int shift // Bar index
);
// Example
double sma20 = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0);
double ema50 = iMA(NULL, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE, 1);
```
### Relative Strength Index - iRSI
```mql4
double iRSI(
string symbol, // Symbol
int timeframe, // Timeframe
int period, // Averaging period
int applied_price, // Applied price
int shift // Bar index
);
// Example
double rsi = iRSI(NULL, 0, 14, PRICE_CLOSE, 0);
if(rsi < 30) Print("Oversold");
if(rsi > 70) Print("Overbought");
```
### MACD - iMACD
```mql4
double iMACD(
string symbol, // Symbol
int timeframe, // Timeframe
int fast_ema_period, // Fast EMA period (typically 12)
int slow_ema_period, // Slow EMA period (typically 26)
int signal_period, // Signal line period (typically 9)
int applied_price, // Applied price
int mode, // Line index: MODE_MAIN or MODE_SIGNAL
int shift // Bar index
);
// Example
double macdMain = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 0);
double macdSignal = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE, MODE_SIGNAL, 0);
if(macdMain > macdSignal) Print("Bullish crossover");
```
### Stochastic Oscillator - iStochastic
```mql4
double iStochastic(
string symbol, // Symbol
int timeframe, // Timeframe
int Kperiod, // %K period
int Dperiod, // %D period (signal)
int slowing, // Slowing
int method, // MA method for smoothing
int price_field, // Price field: 0 = Low/High, 1 = Close/Close
int mode, // MODE_MAIN (%K) or MODE_SIGNAL (%D)
int shift // Bar index
);
// Example
double stochK = iStochastic(NULL, 0, 5, 3, 3, MODE_SMA, 0, MODE_MAIN, 0);
double stochD = iStochastic(NULL, 0, 5, 3, 3, MODE_SMA, 0, MODE_SIGNAL, 0);
```
### Bollinger Bands - iBands
```mql4
double iBands(
string symbol, // Symbol
int timeframe, // Timeframe
int period, // Averaging period
double deviation, // Standard deviation multiplier
int bands_shift, // Horizontal shift (bars)
int applied_price, // Applied price
int mode, // MODE_MAIN (middle), MODE_UPPER, MODE_LOWER
int shift // Bar index
);
// Example
double upper = iBands(NULL, 0, 20, 2.0, 0, PRICE_CLOSE, MODE_UPPER, 0);
double middle = iBands(NULL, 0, 20, 2.0, 0, PRICE_CLOSE, MODE_MAIN, 0);
double lower = iBands(NULL, 0, 20, 2.0, 0, PRICE_CLOSE, MODE_LOWER, 0);
```
### Average True Range - iATR
```mql4
double iATR(
string symbol, // Symbol
int timeframe, // Timeframe
int period, // Averaging period
int shift // Bar index
);
// Example
double atr = iATR(NULL, 0, 14, 0);
double dynamicSL = NormalizeDouble(Ask - atr * 1.5, Digits);
```
### Custom Indicator - iCustom
```mql4
double iCustom(
string symbol, // Symbol
int timeframe, // Timeframe
string name, // Custom indicator file name (without .ex4)
... // Indicator input parameters (in order)
int mode, // Buffer index (0-based)
int shift // Bar index
);
// Example: Call a custom indicator "SuperTrend" with inputs (10, 3.0)
double superTrend = iCustom(NULL, 0, "SuperTrend", 10, 3.0, 0, 0);
// The parameter order must match the indicator's input parameters exactly.
// mode corresponds to the SetIndexBuffer() index in the indicator.
```
### MA Method Constants
| Constant | Value | Description |
|----------|-------|-------------|
| `MODE_SMA` | 0 | Simple Moving Average |
| `MODE_EMA` | 1 | Exponential Moving Average |
| `MODE_SMMA` | 2 | Smoothed Moving Average |
| `MODE_LWMA` | 3 | Linear Weighted Moving Average |
### Applied Price Constants
| Constant | Value | Description |
|----------|-------|-------------|
| `PRICE_CLOSE` | 0 | Close price |
| `PRICE_OPEN` | 1 | Open price |
| `PRICE_HIGH` | 2 | High price |
| `PRICE_LOW` | 3 | Low price |
| `PRICE_MEDIAN` | 4 | (High + Low) / 2 |
| `PRICE_TYPICAL` | 5 | (High + Low + Close) / 3 |
| `PRICE_WEIGHTED` | 6 | (High + Low + Close + Close) / 4 |
### Timeframe Constants
| Constant | Value | Description |
|----------|-------|-------------|
| `PERIOD_CURRENT` | 0 | Current chart timeframe |
| `PERIOD_M1` | 1 | 1 minute |
| `PERIOD_M5` | 5 | 5 minutes |
| `PERIOD_M15` | 15 | 15 minutes |
| `PERIOD_M30` | 30 | 30 minutes |
| `PERIOD_H1` | 60 | 1 hour |
| `PERIOD_H4` | 240 | 4 hours |
| `PERIOD_D1` | 1440 | Daily |
| `PERIOD_W1` | 10080 | Weekly |
| `PERIOD_MN1` | 43200 | Monthly |
---
## Order Management
### Order Types
| Constant | Value | Description |
|----------|-------|-------------|
| `OP_BUY` | 0 | Market buy order |
| `OP_SELL` | 1 | Market sell order |
| `OP_BUYLIMIT` | 2 | Buy limit pending order (below market) |
| `OP_SELLLIMIT` | 3 | Sell limit pending order (above market) |
| `OP_BUYSTOP` | 4 | Buy stop pending order (above market) |
| `OP_SELLSTOP` | 5 | Sell stop pending order (below market) |
### Trading Functions
```mql4
// OrderSend - Returns ticket on success, -1 on failure
int OrderSend(string symbol, int cmd, double volume, double price,
int slippage, double stoploss, double takeprofit,
string comment, int magic, datetime expiration, color arrow_color);
// OrderModify - Returns true on success
bool OrderModify(int ticket, double price, double stoploss,
double takeprofit, datetime expiration, color arrow_color);
// OrderClose - Close market order (can be partial)
bool OrderClose(int ticket, double lots, double price, int slippage, color Color);
// OrderDelete - Delete pending order only
bool OrderDelete(int ticket, color Color);
// OrderSelect - Must call before reading order properties
bool OrderSelect(int index, int select, int pool);
// select: SELECT_BY_POS or SELECT_BY_TICKET
// pool: MODE_TRADES or MODE_HISTORY
int OrdersTotal(); // Open + pending count
int OrdersHistoryTotal(); // Closed + deleted count
```
### Order Information Functions
After `OrderSelect()`, these return info about the selected order:
| Function | Return | Description |
|----------|--------|-------------|
| `OrderTicket()` | `int` | Ticket number |
| `OrderType()` | `int` | Order type (OP_BUY, etc.) |
| `OrderLots()` | `double` | Volume |
| `OrderOpenPrice()` | `double` | Open price |
| `OrderClosePrice()` | `double` | Close price (0 if open) |
| `OrderOpenTime()` | `datetime` | Open time |
| `OrderCloseTime()` | `datetime` | Close time (0 if open) |
| `OrderStopLoss()` | `double` | Stop loss |
| `OrderTakeProfit()` | `double` | Take profit |
| `OrderMagicNumber()` | `int` | Magic number |
| `OrderComment()` | `string` | Comment |
| `OrderSymbol()` | `string` | Symbol |
| `OrderProfit()` | `double` | Net profit |
| `OrderCommission()` | `double` | Commission |
| `OrderSwap()` | `double` | Swap |
| `OrderExpiration()` | `datetime` | Expiration |
> **Production patterns** (retry logic, order loops, close all, error handling): See [trading-operations.md](trading-operations.md)
---
## Market Information
### MarketInfo
```mql4
double MarketInfo(string symbol, int type);
```
| Constant | Description |
|----------|-------------|
| `MODE_SPREAD` | Spread in points |
| `MODE_STOPLEVEL` | Minimum stop level in points |
| `MODE_LOTSIZE` | Contract size (e.g., 100000 for forex) |
| `MODE_TICKVALUE` | Tick value in account currency |
| `MODE_TICKSIZE` | Tick size (minimum price change) |
| `MODE_SWAPLONG` | Swap for long positions |
| `MODE_SWAPSHORT` | Swap for short positions |
| `MODE_MINLOT` | Minimum lot size |
| `MODE_LOTSTEP` | Lot size step |
| `MODE_MAXLOT` | Maximum lot size |
| `MODE_MARGINREQUIRED` | Margin required for 1 lot |
| `MODE_DIGITS` | Number of decimal digits |
| `MODE_POINT` | Point size |
| `MODE_FREEZELEVEL` | Freeze distance in points (cannot modify orders within this distance) |
```mql4
double spread = MarketInfo(_Symbol, MODE_SPREAD);
double stopLevel = MarketInfo(_Symbol, MODE_STOPLEVEL);
double minLot = MarketInfo(_Symbol, MODE_MINLOT);
double lotStep = MarketInfo(_Symbol, MODE_LOTSTEP);
double maxLot = MarketInfo(_Symbol, MODE_MAXLOT);
double tickVal = MarketInfo(_Symbol, MODE_TICKVALUE);
double tickSize = MarketInfo(_Symbol, MODE_TICKSIZE);
double margin1 = MarketInfo(_Symbol, MODE_MARGINREQUIRED);
int digits = (int)MarketInfo(_Symbol, MODE_DIGITS);
double point = MarketInfo(_Symbol, MODE_POINT);
```
### SymbolInfoDouble (Modern Replacement)
```mql4
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
long digits = SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
```
---
## Account Functions
| Function | Return Type | Description |
|----------|-------------|-------------|
| `AccountBalance()` | `double` | Account balance |
| `AccountEquity()` | `double` | Account equity (balance + floating P/L) |
| `AccountFreeMargin()` | `double` | Free margin available |
| `AccountMargin()` | `double` | Margin currently used |
| `AccountProfit()` | `double` | Total floating profit/loss |
| `AccountCurrency()` | `string` | Account currency (e.g., "USD") |
| `AccountNumber()` | `int` | Account number |
| `AccountCompany()` | `string` | Broker company name |
| `AccountLeverage()` | `int` | Account leverage (e.g., 100) |
| `AccountName()` | `string` | Account holder name |
| `AccountServer()` | `string` | Trade server name |
| `AccountStopoutLevel()` | `int` | Stop-out level (margin %) |
| `AccountStopoutMode()` | `int` | Stop-out mode (0=percent, 1=money) |
| `AccountInfoDouble(prop)` | `double` | Modern replacement |
| `AccountInfoInteger(prop)` | `long` | Modern replacement |
| `AccountInfoString(prop)` | `string` | Modern replacement |
```mql4
// Risk calculation example
double riskPercent = 2.0;
double balance = AccountBalance();
double riskAmount = balance * riskPercent / 100.0;
Print("Account: ", AccountNumber(), " at ", AccountCompany());
Print("Balance: ", balance, " ", AccountCurrency());
Print("Equity: ", AccountEquity());
Print("Margin: ", AccountMargin());
Print("Free: ", AccountFreeMargin());
Print("Leverage: 1:", AccountLeverage());
```
---
## Preprocessor Directives
### #property
```mql4
// Common EA/indicator/script properties
#property copyright "Copyright 2024, Author"
#property link "https://www.example.com"
#property version "1.00"
#property description "My Expert Advisor"
#property strict // Enable strict compilation mode (recommended)
// Indicator-specific
#property indicator_chart_window
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1 clrBlue
#property indicator_color2 clrRed
#property indicator_color3 clrGreen
#property indicator_width1 2
#property indicator_style1 STYLE_SOLID
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_level1 20
#property indicator_level2 80
#property indicator_levelcolor clrSilver
// Script-specific
#property show_inputs // Show inputs dialog
#property show_confirm // Show confirmation dialog
// Library-specific
#property library // Mark as library
```
### #include
```mql4
// Include from MQL4/Include/ directory
#include <stdlib.mqh> // Standard library (ErrorDescription, etc.)
#include <stderror.mqh> // Error code constants
// Include from same directory as the source file
#include "MyHelpers.mqh"
// Include guards (manual, MQL4 has no #pragma once)
#ifndef MY_HELPERS_MQH
#define MY_HELPERS_MQH
// ... header content ...
#endif
```
### #define
```mql4
// Simple constants
#define EA_NAME "SuperScalper"
#define EA_VERSION "2.1"
#define MAGIC 20240115
// Macros with parameters
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define PIP(x) ((x) * g_pipSize)
// Multi-line macro (use backslash)
#define LOG(msg) \
Print(__FILE__, " Line ", __LINE__, ": ", msg)
// Predefined macros
// __FILE__ - Current file name
// __LINE__ - Current line number
// __FUNCTION__ - Current function name
// __DATETIME__ - Compilation date/time
// __MQLBUILD__ - MQL compiler build number
```
### #import
```mql4
// Import from compiled MQL4 library (.ex4)
#import "MyLibrary.ex4"
double CalculateRisk(double percent, int stopPoints);
int CountSignals(string symbol, int timeframe);
#import
// Import from Windows DLL
#import "kernel32.dll"
int GetTickCount();
void Sleep(int milliseconds);
#import
#import "user32.dll"
int MessageBoxW(int hWnd, string text, string caption, int type);
#import
```
---
## Error Handling
### Error Functions
```mql4
// int GetLastError()
// Returns the last error code and resets the internal error variable.
int err = GetLastError();
// void ResetLastError()
// Resets the error variable to ERR_NO_ERROR (0).
ResetLastError();
// int _LastError
// Predefined variable holding the last error code (NOT reset by reading).
// string ErrorDescription(int error_code)
// Requires: #include <stdlib.mqh>
#include <stdlib.mqh>
string desc = ErrorDescription(GetLastError());
```
### Trade Error Codes
| Constant | Value | Description |
|----------|-------|-------------|
| `ERR_NO_ERROR` | 0 | No error |
| `ERR_NO_RESULT` | 1 | No error but result unknown |
| `ERR_COMMON_ERROR` | 2 | Common error |
| `ERR_INVALID_TRADE_PARAMETERS` | 3 | Invalid trade parameters |
| `ERR_SERVER_BUSY` | 4 | Trade server is busy |
| `ERR_OLD_VERSION` | 5 | Old client terminal version |
| `ERR_NO_CONNECTION` | 6 | No connection to server |
| `ERR_NOT_ENOUGH_RIGHTS` | 7 | Insufficient rights |
| `ERR_TOO_FREQUENT_REQUESTS` | 8 | Too frequent requests |
| `ERR_MALFUNCTIONAL_TRADE` | 9 | Malfunctional trade operation |
| `ERR_ACCOUNT_DISABLED` | 64 | Account disabled |
| `ERR_INVALID_ACCOUNT` | 65 | Invalid account |
| `ERR_TRADE_TIMEOUT` | 128 | Trade timeout |
| `ERR_INVALID_PRICE` | 129 | Invalid price |
| `ERR_INVALID_STOPS` | 130 | Invalid stops |
| `ERR_INVALID_TRADE_VOLUME` | 131 | Invalid lot size |
| `ERR_MARKET_CLOSED` | 132 | Market closed |
| `ERR_TRADE_DISABLED` | 133 | Trading disabled |
| `ERR_NOT_ENOUGH_MONEY` | 134 | Not enough money |
| `ERR_PRICE_CHANGED` | 135 | Price changed (requote) |
| `ERR_OFF_QUOTES` | 136 | Off quotes |
| `ERR_BROKER_BUSY` | 137 | Broker busy |
| `ERR_REQUOTE` | 138 | Requote |
| `ERR_ORDER_LOCKED` | 139 | Order locked |
| `ERR_LONG_POSITIONS_ONLY_ALLOWED` | 140 | Long positions only |
| `ERR_TOO_MANY_REQUESTS` | 141 | Too many requests |
| `ERR_TRADE_MODIFY_DENIED` | 145 | Modification denied (too close to market) |
| `ERR_TRADE_CONTEXT_BUSY` | 146 | Trade context busy |
| `ERR_TRADE_EXPIRATION_DENIED` | 147 | Expiration denied by broker |
| `ERR_TRADE_TOO_MANY_ORDERS` | 148 | Too many open orders |
| `ERR_TRADE_HEDGE_PROHIBITED` | 149 | Hedging prohibited |
| `ERR_TRADE_PROHIBITED_BY_FIFO` | 150 | FIFO rule violation |
### Runtime Error Codes
| Constant | Value | Description |
|----------|-------|-------------|
| `ERR_NO_MQLERROR` | 4000 | No MQL error |
| `ERR_WRONG_FUNCTION_POINTER` | 4001 | Wrong function pointer |
| `ERR_ARRAY_INDEX_OUT_OF_RANGE` | 4002 | Array index out of range |
| `ERR_NO_MEMORY_FOR_CALL_STACK` | 4003 | No memory for function call stack |
| `ERR_RECURSIVE_STACK_OVERFLOW` | 4004 | Recursive stack overflow |
| `ERR_NOT_ENOUGH_STACK_FOR_PARAM` | 4005 | Not enough stack for parameter |
| `ERR_NO_MEMORY_FOR_PARAM_STRING` | 4006 | No memory for parameter string |
| `ERR_NO_MEMORY_FOR_TEMP_STRING` | 4007 | No memory for temp string |
| `ERR_NOT_INITIALIZED_STRING` | 4008 | Not initialized string |
| `ERR_NOT_INITIALIZED_ARRAYSTRING` | 4009 | Not initialized string array |
| `ERR_NO_MEMORY_FOR_ARRAYSTRING` | 4010 | No memory for string array |
| `ERR_TOO_LONG_STRING` | 4011 | String too long |
| `ERR_ZERO_DIVIDE` | 4013 | Division by zero |
| `ERR_UNKNOWN_COMMAND` | 4014 | Unknown command |
| `ERR_WRONG_JUMP` | 4015 | Wrong jump |
| `ERR_NOT_INITIALIZED_ARRAY` | 4016 | Array not initialized |
| `ERR_CUSTOM_INDICATOR_ERROR` | 4055 | Custom indicator error |
| `ERR_INCOMPATIBLE_ARRAYS` | 4056 | Incompatible arrays |
| `ERR_INVALID_FUNCTION_PARAMSCNT` | 4059 | Invalid function parameters count |
| `ERR_INVALID_FUNCTION_PARAMVALUE` | 4063 | Invalid function parameter value |
| `ERR_STRING_FUNCTION_INTERNAL` | 4062 | String function internal error |
| `ERR_ARRAY_AS_PARAMETER_EXPECTED` | 4065 | Array as parameter expected |
| `ERR_NO_ORDER_SELECTED` | 4105 | No order selected |
| `ERR_UNKNOWN_SYMBOL` | 4106 | Unknown symbol |
| `ERR_INVALID_PRICE_PARAM` | 4107 | Invalid price parameter |
| `ERR_INVALID_TICKET` | 4108 | Invalid ticket |
| `ERR_TRADE_NOT_ALLOWED` | 4109 | Trade not allowed (EA or AutoTrading disabled) |
| `ERR_LONGS_NOT_ALLOWED` | 4110 | Longs not allowed |
| `ERR_SHORTS_NOT_ALLOWED` | 4111 | Shorts not allowed |
| `ERR_OBJECT_ALREADY_EXISTS` | 4200 | Object already exists |
| `ERR_UNKNOWN_OBJECT_PROPERTY` | 4201 | Unknown object property |
| `ERR_OBJECT_DOES_NOT_EXIST` | 4202 | Object does not exist |
| `ERR_UNKNOWN_OBJECT_TYPE` | 4203 | Unknown object type |
### Robust Error Handling Pattern with Retry Logic
```mql4
#include <stdlib.mqh>
int SendOrderWithRetry(string symbol, int cmd, double volume, double price,
int slippage, double sl, double tp, string comment,
int magic, int maxRetries = 5)
{
int ticket = -1;
for(int attempt = 0; attempt < maxRetries; attempt++)
{
ResetLastError();
RefreshRates();
// Update price for market orders
if(cmd == OP_BUY) price = MarketInfo(symbol, MODE_ASK);
if(cmd == OP_SELL) price = MarketInfo(symbol, MODE_BID);
ticket = OrderSend(symbol, cmd, volume, price, slippage,
sl, tp, comment, magic, 0, clrGreen);
if(ticket >= 0)
{
Print("Order sent successfully. Ticket: ", ticket);
return ticket;
}
int err = GetLastError();
Print("OrderSend attempt ", attempt + 1, " failed. Error ", err,
": ", ErrorDescription(err));
switch(err)
{
case ERR_NO_ERROR:
return ticket;
// Retriable errors
case ERR_SERVER_BUSY:
case ERR_TRADE_TIMEOUT:
case ERR_PRICE_CHANGED:
case ERR_REQUOTE:
case ERR_OFF_QUOTES:
case ERR_BROKER_BUSY:
case ERR_TRADE_CONTEXT_BUSY:
case ERR_TOO_FREQUENT_REQUESTS:
case ERR_TOO_MANY_REQUESTS:
Sleep(1000 * (attempt + 1)); // Progressive delay
break;
// Fatal errors - stop retrying
case ERR_INVALID_TRADE_PARAMETERS:
case ERR_INVALID_STOPS:
case ERR_INVALID_TRADE_VOLUME:
case ERR_NOT_ENOUGH_MONEY:
case ERR_TRADE_DISABLED:
case ERR_MARKET_CLOSED:
case ERR_ACCOUNT_DISABLED:
case ERR_TRADE_NOT_ALLOWED:
Print("Fatal trade error. Aborting.");
return -1;
default:
Sleep(500);
break;
}
}
Print("OrderSend failed after ", maxRetries, " attempts");
return -1;
}
```
---
## Common Gotchas
### 1. Double Comparison with NormalizeDouble
Floating-point numbers cannot be compared directly due to precision issues.
```mql4
// WRONG: Direct comparison
if(price1 == price2) // May fail due to floating-point precision
// CORRECT: Use NormalizeDouble
if(NormalizeDouble(price1 - price2, Digits) == 0)
// Or use a helper function
bool IsEqual(double a, double b, int digits)
{
return NormalizeDouble(a - b, digits) == 0;
}
bool IsGreater(double a, double b, int digits)
{
return NormalizeDouble(a - b, digits) > 0;
}
// IMPORTANT: Always NormalizeDouble prices before using in OrderSend/OrderModify
double sl = NormalizeDouble(Ask - 50 * Point, Digits);
double tp = NormalizeDouble(Ask + 100 * Point, Digits);
```
### 2. Four-Digit vs Five-Digit Broker Detection
Some brokers use 4-digit quotes (EURUSD = 1.2345), others use 5-digit (EURUSD = 1.23456). For JPY pairs, it is 2 vs 3 digits. You must handle this for pip calculations and slippage.
```mql4
double PipSize;
int PipDigits;
int SlippagePoints;
int OnInit()
{
if(Digits == 5 || Digits == 3) // 5-digit broker
{
PipSize = Point * 10;
PipDigits = Digits - 1;
SlippagePoints = 30; // 3.0 pips
}
else // 4-digit broker
{
PipSize = Point;
PipDigits = Digits;
SlippagePoints = 3; // 3.0 pips
}
// Now use PipSize for calculations
double stopLoss = 50 * PipSize; // 50 pips regardless of broker type
return INIT_SUCCEEDED;
}
```
### 3. Slippage in Points, Not Pips
`OrderSend()` slippage parameter is always in **points** (the smallest price unit), not pips.
```mql4
// For a 5-digit broker where 1 pip = 10 points:
// If you want 3 pips slippage, use slippage = 30
int slippage = 3; // This is 3 POINTS = 0.3 pips on 5-digit!
// Correct for 3 pips:
int slippagePips = 3;
int slippagePoints = (Digits == 5 || Digits == 3) ? slippagePips * 10 : slippagePips;
```
### 4. RefreshRates() for Stale Prices
```mql4
// After long calculations or Sleep(), Ask/Bid may be stale
Sleep(5000);
RefreshRates(); // Update Ask, Bid, and predefined arrays
double freshAsk = Ask;
```
### 5. Stop Level Violations
The broker imposes a minimum distance for stop loss and take profit from the current price.
```mql4
double stopLevel = MarketInfo(_Symbol, MODE_STOPLEVEL) * Point;
double freezeLevel = MarketInfo(_Symbol, MODE_FREEZELEVEL) * Point;
// Ensure SL/TP are far enough from the current price
double sl = Ask - 50 * Point;
if(MathAbs(Ask - sl) < stopLevel)
{
sl = NormalizeDouble(Ask - stopLevel - Point, Digits);
Print("SL adjusted to meet stop level requirement");
}
```
### 6. Trade Context Busy (IsTradeAllowed)
Only one EA can send trade requests at a time in MT4.
```mql4
// Check if trading is allowed
if(!IsTradeAllowed())
{
Print("Trade context is busy. Waiting...");
// Wait for trade context
int attempts = 0;
while(!IsTradeAllowed() && attempts < 50)
{
Sleep(100);
attempts++;
}
if(!IsTradeAllowed())
{
Print("Trade context still busy after waiting. Aborting.");
return;
}
}
// Also check these conditions
if(!IsConnected()) { Print("Not connected to server"); return; }
if(!IsTradeAllowed()) { Print("AutoTrading is disabled"); return; }
if(IsStopped()) { Print("EA is being stopped"); return; }
if(!IsExpertEnabled()) { Print("Expert Advisors disabled"); return; }
```
### 7. ECN Two-Step Order Placement
ECN/STP brokers often reject orders with SL/TP set at the time of opening. You must open the order first without SL/TP, then modify.
```mql4
bool OpenOrderECN(int type, double lots, double sl, double tp,
string comment, int magic)
{
double price = (type == OP_BUY) ? Ask : Bid;
// Step 1: Open order WITHOUT SL/TP
int ticket = OrderSend(_Symbol, type, lots, price, 3,
0, 0, comment, magic, 0, clrGreen);
if(ticket < 0)
{
Print("OrderSend failed: ", GetLastError());
return false;
}
// Step 2: Modify to add SL/TP
if(sl != 0 || tp != 0)
{
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
if(!OrderModify(ticket, price, sl, tp, 0, clrBlue))
{
Print("OrderModify failed: ", GetLastError(),
" - Order is open but without SL/TP!");
}
}
return true;
}
```
---
## File Operations
### File Modes
| Constant | Description |
|----------|-------------|
| `FILE_READ` | Open for reading |
| `FILE_WRITE` | Open for writing (creates/overwrites) |
| `FILE_BIN` | Binary mode |
| `FILE_CSV` | CSV mode (comma-separated) |
| `FILE_TXT` | Text mode |
| `FILE_COMMON` | Use common data folder (shared between terminals) |
| `FILE_ANSI` | ANSI encoding |
| `FILE_UNICODE` | Unicode encoding |
**Security:** File operations are sandboxed to `MQL4/Files/` directory (or `Terminal/Common/Files/` with `FILE_COMMON`). You cannot access files outside these directories.
### File Functions
```mql4
// int FileOpen(string filename, int flags, short delimiter=';', uint codepage=CP_ACP)
// Returns file handle (>= 0) or INVALID_HANDLE (-1) on failure.
int handle = FileOpen("data.csv", FILE_WRITE|FILE_CSV, ',');
// void FileClose(int handle)
FileClose(handle);
// uint FileWrite(int handle, ...)
// Writes data to a CSV or TXT file. Returns number of bytes written.
FileWrite(handle, "Symbol", "Price", "Time");
// string FileReadString(int handle, int length=0)
string line = FileReadString(handle);
// double FileReadDouble(int handle, int size=DOUBLE_VALUE)
double value = FileReadDouble(handle);
// int FileReadInteger(int handle, int size=INT_VALUE)
int num = FileReadInteger(handle);
// bool FileIsEnding(int handle)
while(!FileIsEnding(handle))
{
string data = FileReadString(handle);
}
// bool FileDelete(string filename, int common_flag=0)
FileDelete("old_data.csv");
// bool FileIsExist(string filename, int common_flag=0)
if(FileIsExist("config.txt"))
{
// Read config
}
```
### Write CSV File Example
```mql4
void SaveTradeHistory()
{
int handle = FileOpen("trade_history.csv", FILE_WRITE|FILE_CSV, ',');
if(handle == INVALID_HANDLE)
{
Print("FileOpen failed. Error: ", GetLastError());
return;
}
// Header
FileWrite(handle, "Ticket", "Symbol", "Type", "Lots",
"OpenPrice", "ClosePrice", "Profit", "OpenTime", "CloseTime");
// Data
for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
if(OrderSymbol() != _Symbol) continue;
string typeStr = (OrderType() == OP_BUY) ? "BUY" : "SELL";
FileWrite(handle,
OrderTicket(),
OrderSymbol(),
typeStr,
DoubleToString(OrderLots(), 2),
DoubleToString(OrderOpenPrice(), Digits),
DoubleToString(OrderClosePrice(), Digits),
DoubleToString(OrderProfit(), 2),
TimeToString(OrderOpenTime()),
TimeToString(OrderCloseTime()));
}
FileClose(handle);
Print("Trade history saved to MQL4/Files/trade_history.csv");
}
```
### Read CSV File Example
```mql4
void LoadSettings()
{
if(!FileIsExist("settings.csv"))
{
Print("Settings file not found");
return;
}
int handle = FileOpen("settings.csv", FILE_READ|FILE_CSV, ',');
if(handle == INVALID_HANDLE)
{
Print("FileOpen failed: ", GetLastError());
return;
}
while(!FileIsEnding(handle))
{
string key = FileReadString(handle);
string value = FileReadString(handle);
Print("Setting: ", key, " = ", value);
}
FileClose(handle);
}
```
### Write/Read Binary File Example
```mql4
// Write binary
void SaveBuffer(double &buffer[], int count)
{
int handle = FileOpen("buffer.bin", FILE_WRITE|FILE_BIN);
if(handle == INVALID_HANDLE) return;
FileWriteInteger(handle, count, INT_VALUE);
for(int i = 0; i < count; i++)
FileWriteDouble(handle, buffer[i], DOUBLE_VALUE);
FileClose(handle);
}
// Read binary
void LoadBuffer(double &buffer[])
{
int handle = FileOpen("buffer.bin", FILE_READ|FILE_BIN);
if(handle == INVALID_HANDLE) return;
int count = FileReadInteger(handle, INT_VALUE);
ArrayResize(buffer, count);
for(int i = 0; i < count; i++)
buffer[i] = FileReadDouble(handle, DOUBLE_VALUE);
FileClose(handle);
}
```
---
## WebRequest
Two function signatures. **Whitelist URL first:** Tools > Options > Expert Advisors. Only in EAs/Scripts (not indicators, not Strategy Tester).
```mql4
// Variant 1: Simple (cookie/referer)
int WebRequest(const string method, const string url,
const string cookie, const string referer,
int timeout, const char &data[], int data_size,
char &result[], string &headers);
// Variant 2: Custom headers (recommended)
int WebRequest(const string method, const string url,
const string headers, int timeout,
const char &data[], char &result[], string &result_headers);
// Returns: HTTP status code (200, 404, etc.) or -1 on error.
```
**Key notes:**
- `StringToCharArray()` adds null terminator; subtract 1 from size for binary data
- Error 4060 = URL not whitelisted
- HTTPS supported and recommended
> **Full examples** (GET, POST/JSON, CHttpClient class, Node.js patterns, error handling): See [external-communication.md](external-communication.md)
---
## Utility Functions
### Mathematical Functions
```mql4
double MathAbs(double value); // Absolute value
double MathCeil(double value); // Round up
double MathFloor(double value); // Round down
double MathRound(double value); // Round to nearest
double MathMax(double a, double b); // Maximum
double MathMin(double a, double b); // Minimum
double MathPow(double base, double exp); // Power
double MathSqrt(double value); // Square root
double MathLog(double value); // Natural logarithm
double MathExp(double value); // e^x
double MathMod(double a, double b); // Modulo for doubles
int MathRand(); // Random number 0-32767
void MathSrand(int seed); // Seed random generator
double NormalizeDouble(double value, int digits); // Round to digits
```
### Date and Time Functions
```mql4
datetime TimeCurrent(); // Server time (last known)
datetime TimeLocal(); // Local computer time
datetime TimeGMT(); // GMT time
int TimeYear(datetime t); // Year
int TimeMonth(datetime t); // Month (1-12)
int TimeDay(datetime t); // Day of month (1-31)
int TimeHour(datetime t); // Hour (0-23)
int TimeMinute(datetime t); // Minute (0-59)
int TimeSeconds(datetime t); // Seconds (0-59)
int TimeDayOfWeek(datetime t); // Day of week (0=Sunday)
int TimeDayOfYear(datetime t); // Day of year (1-366)
string TimeToString(datetime t, int mode=TIME_DATE|TIME_MINUTES);
datetime StringToTime(string value);
// Example: Trade only during London session
int hour = TimeHour(TimeCurrent());
if(hour >= 8 && hour < 16) // London hours
{
// Trading allowed
}
```
### Chart Functions
```mql4
long ChartID(); // Current chart ID
bool ChartSetInteger(long chart_id, int prop, long value);
bool ChartSetDouble(long chart_id, int prop, double value);
bool ChartSetString(long chart_id, int prop, string value);
void ChartRedraw(long chart_id=0); // Redraw chart
// Example: Add comment to chart
Comment("Balance: ", AccountBalance(), "\n",
"Equity: ", AccountEquity(), "\n",
"Spread: ", MarketInfo(_Symbol, MODE_SPREAD));
```
### Object Functions
```mql4
bool ObjectCreate(string name, int type, int window, datetime time1, double price1, ...);
bool ObjectDelete(string name);
bool ObjectSet(string name, int prop, double value);
bool ObjectSetText(string name, string text, int font_size, string font, color text_color);
// Example: Draw horizontal line
ObjectCreate("SupportLine", OBJ_HLINE, 0, 0, 1.23456);
ObjectSet("SupportLine", OBJPROP_COLOR, clrBlue);
ObjectSet("SupportLine", OBJPROP_WIDTH, 2);
ObjectSet("SupportLine", OBJPROP_STYLE, STYLE_DASH);
```
### Print and Alert
```mql4
Print("Message to Experts log"); // Experts tab in Terminal
Alert("Alert popup message"); // Pop-up dialog
Comment("On-chart comment"); // Displayed on chart
SendNotification("Push notification"); // Mobile push (if configured)
SendMail("Subject", "Email body"); // Email (if configured)
PlaySound("alert.wav"); // Play sound file
```
### Miscellaneous
```mql4
void Sleep(int milliseconds); // Pause execution (not in indicators!)
bool IsTesting(); // Running in Strategy Tester?
bool IsOptimization(); // Running optimization?
bool IsVisualMode(); // Visual mode in tester?
bool IsDemo(); // Demo account?
bool IsConnected(); // Connected to server?
bool IsTradeAllowed(); // Trade context free?
bool IsExpertEnabled(); // Expert Advisors enabled?
bool IsStopped(); // EA being terminated?
bool IsDllsAllowed(); // DLL imports allowed?
string TerminalInfoString(int prop); // Terminal info
string Symbol(); // Current symbol (same as _Symbol)
int Period(); // Current period (same as _Period)
int UninitializeReason(); // Deinitialization reason
void ExpertRemove(); // Remove EA from chart
```
---
## Global Terminal Variables
Not to be confused with global program variables. These are key-value pairs stored at the terminal level, shared between programs.
```mql4
// bool GlobalVariableSet(string name, double value)
// Creates or updates a global terminal variable.
GlobalVariableSet("LastTradeTime_EURUSD", (double)TimeCurrent());
// double GlobalVariableGet(string name)
// Returns the value. Returns 0 if not found (check with GlobalVariableCheck).
double lastTime = GlobalVariableGet("LastTradeTime_EURUSD");
// bool GlobalVariableCheck(string name)
// Returns true if the variable exists.
if(GlobalVariableCheck("LastTradeTime_EURUSD"))
{
datetime t = (datetime)GlobalVariableGet("LastTradeTime_EURUSD");
}
// bool GlobalVariableDel(string name)
GlobalVariableDel("LastTradeTime_EURUSD");
// bool GlobalVariableSetOnCondition(string name, double new_value, double check_value)
// Atomic set: only sets if the current value matches check_value. Useful for locking.
// Returns true if the value was set.
if(GlobalVariableSetOnCondition("TradeLock", 1.0, 0.0))
{
// We acquired the lock
// ... trade ...
GlobalVariableSet("TradeLock", 0.0); // Release lock
}
```