Files
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

63 KiB

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

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
// 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

enum ENUM_SIGNAL
{
   SIGNAL_NONE = 0,   // No signal
   SIGNAL_BUY  = 1,   // Buy signal
   SIGNAL_SELL = -1    // Sell signal
};

ENUM_SIGNAL signal = SIGNAL_BUY;

Structures

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

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.

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.

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.

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.

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.

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

double lots = (AccountBalance() > 10000) ? 0.2 : 0.1;
string direction = (signal == SIGNAL_BUY) ? "BUY" : "SELL";

Arrays

Static Arrays

Fixed size, determined at compile time.

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().

double buffer[];
ArrayResize(buffer, 100);
buffer[0] = 1.23456;

Multi-Dimensional Arrays

double matrix[3][4];        // Static 3x4 matrix
double grid[][5];            // Dynamic first dimension, fixed second
ArrayResize(grid, 10);       // Now 10x5

Array Functions

// 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

// 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

// 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.

//+------------------------------------------------------------------+
//|                                                      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

#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.

#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

//+------------------------------------------------------------------+
//|                                                 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

// 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.

//+------------------------------------------------------------------+
//|                                                    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.

//+------------------------------------------------------------------+
//|                                                  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

// 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.

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

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

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

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

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

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

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

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

// 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


Market Information

MarketInfo

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)
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)

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
// 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

// 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

// 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

// 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

// 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

// 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

#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.

// 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.

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.

// 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

// 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.

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.

// 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.

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

// 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

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

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

// 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).

// 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


Utility Functions

Mathematical Functions

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

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

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

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

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

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.

// 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
}