MQL4/.github/instructions/mql4-syntax-critical.instructions.md
Rahul Dhangar 740e967470 Add comprehensive MQL4 instruction files and update main copilot instructions
- Add mql4-syntax-critical.instructions.md: Complete syntax rules for MQL4
- Add mql4-experts.instructions.md: Expert Advisor development guidelines
- Add mql4-indicators.instructions.md: Custom indicator development guidelines
- Add mql4-scripts.instructions.md: Script development guidelines
- Update copilot-instructions.md: Add language-specific instruction routing
- Include MQL4 vs MQL5 comparison table and workflow guidance
- Based on real-world experience from MultiTimeframeZone EA development
2025-11-04 04:07:37 +05:30

16 KiB

applyTo
**/*.mq4,**/MQL4/**/*.mqh

CRITICAL MQL4 Syntax Rules (Mandatory for All MQL4 Code)

⚠️ Overview

MQL4 is a C-like language with specific syntax requirements that differ from both standard C++ and MQL5. These rules are mandatory for all _Thivyam MQL4 development to ensure code compiles without errors.


1. Loop Variable Declaration

Rule

NEVER declare variables inside for-loop initializers
ALWAYS declare loop counters BEFORE the for statement

Why

MQL4 does not support C99/C++11 style variable declaration in for-loop initializers.

Examples

// ✅ CORRECT
int i;
for(i = 0; i < 10; i++)
{
   Print(i);
}

// Multiple nested loops
int i, j;
for(i = 0; i < ArraySize(h4_ZoneHighs); i++)
{
   for(j = 0; j < ArraySize(m30_ZoneHighs); j++)
   {
      // Process zones
   }
}

// ❌ WRONG - Causes compilation errors
for(int i = 0; i < 10; i++)  // Error: 'i' - undeclared identifier
{
   Print(i);
}

Additional Rules

  • Use post-increment (i++) not pre-increment (++i)
  • Avoid const on loop bound variables unless necessary
  • Declare all loop variables at the beginning of the function or at the nearest scope

Error Messages You'll See

  • 'i' - undeclared identifier
  • 'for' - some operator expected
  • expression expected

2. Array Declarations and Sizing

Rule

Declare arrays with size or use dynamic arrays for different purposes
Be explicit about array sizes when needed

Examples

// ✅ CORRECT - Fixed-size arrays for globals
double h4_ZoneHighs[50];
string h4_ZoneTypes[50];
int h4_ZoneCount = 0;

// ✅ CORRECT - Dynamic arrays for temporary data
double prices[];
ArrayResize(prices, 100);

// ✅ CORRECT - Array for iHigh, iLow, etc.
double high[], low[];
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);

// ❌ WRONG - Don't mix fixed and dynamic concepts
double data[50];
ArrayResize(data, 100);  // May cause issues

Best Practices

  • Use fixed-size arrays for zone storage with a counter
  • Use dynamic arrays for temporary calculations or data buffers
  • Always check array bounds before accessing elements
  • Use ArraySize() to get the actual size of an array

3. Order Functions and Trade Operations

Rule

Use MQL4 trade functions correctly with proper error handling
Always check return values and use GetLastError()

Examples

// ✅ CORRECT - OrderSend with full error handling
int ticket = OrderSend(Symbol(), OP_BUYLIMIT, lotSize, entryPrice, slippage,
                       stopLoss, takeProfit, "Comment", magicNumber, 0, clrGreen);

if(ticket > 0)
{
   Print("Order placed successfully. Ticket: ", ticket);
}
else
{
   int error = GetLastError();
   Print("OrderSend failed with error: ", error, " - ", ErrorDescription(error));
}

// ✅ CORRECT - OrderModify with validation
if(OrderSelect(ticket, SELECT_BY_TICKET))
{
   bool modified = OrderModify(ticket, OrderOpenPrice(), newSL, newTP, 0, clrBlue);
   if(!modified)
   {
      int error = GetLastError();
      Print("OrderModify failed: ", error);
   }
}

// ❌ WRONG - No error checking
OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "", 0, 0, clrGreen);
// What if it fails?

Order Type Constants

// Pending Orders
OP_BUYLIMIT   // Buy when price drops to entry
OP_SELLLIMIT  // Sell when price rises to entry
OP_BUYSTOP    // Buy when price rises to entry
OP_SELLSTOP   // Sell when price drops to entry

// Market Orders
OP_BUY        // Immediate buy at Ask
OP_SELL       // Immediate sell at Bid

Common Errors

  • Error 130 (Invalid Stops): SL/TP too close to entry, violates MODE_STOPLEVEL
  • Error 131 (Invalid Volume): Lot size violates MODE_MINLOT or MODE_MAXLOT
  • Error 134 (Not Enough Money): Insufficient margin
  • Error 129 (Invalid Price): Price is stale, requote needed

4. Price and Market Information Functions

Rule

Use correct market info mode constants
Always normalize prices to Digits precision

Examples

// ✅ CORRECT - Getting market information
double ask = MarketInfo(Symbol(), MODE_ASK);
double bid = MarketInfo(Symbol(), MODE_BID);
double point = MarketInfo(Symbol(), MODE_POINT);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL);
double spread = MarketInfo(Symbol(), MODE_SPREAD);

// ✅ CORRECT - Normalizing prices
double price = NormalizeDouble(calculatedPrice, Digits);
double lots = NormalizeDouble(calculatedLots, 2);

// ✅ CORRECT - Getting historical data
double high = iHigh(Symbol(), PERIOD_H4, barIndex);
double low = iLow(Symbol(), PERIOD_M30, barIndex);
double open = iOpen(Symbol(), PERIOD_M15, barIndex);
double close = iClose(Symbol(), PERIOD_CURRENT, barIndex);

// ❌ WRONG - Using undefined constants
double info = MarketInfo(Symbol(), MODE_INVALID);  // No such constant

Normalization Best Practice

// Create normalization function
double NormalizePrice(double price)
{
   return NormalizeDouble(price, Digits);
}

double NormalizeLots(double lots)
{
   double minLot = MarketInfo(Symbol(), MODE_MINLOT);
   double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
   double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
   
   lots = NormalizeDouble(lots, 2);
   lots = MathMax(lots, minLot);
   lots = MathMin(lots, maxLot);
   
   return NormalizeDouble(lots, 2);
}

5. Object Properties and Chart Objects

Rule

Verify object property constant names in MQL4 documentation
Property names can differ from what you expect

Examples

// ✅ CORRECT - Rectangle object
ObjectCreate(0, "MyRectangle", OBJ_RECTANGLE, 0, time1, price1, time2, price2);
ObjectSetInteger(0, "MyRectangle", OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, "MyRectangle", OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, "MyRectangle", OBJPROP_WIDTH, 2);
ObjectSetInteger(0, "MyRectangle", OBJPROP_BACK, false);
ObjectSetInteger(0, "MyRectangle", OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, "MyRectangle", OBJPROP_RAY_RIGHT, true);

// ✅ CORRECT - Text object
ObjectCreate(0, "MyLabel", OBJ_TEXT, 0, time, price);
ObjectSetString(0, "MyLabel", OBJPROP_TEXT, "Zone Label");
ObjectSetInteger(0, "MyLabel", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, "MyLabel", OBJPROP_FONTSIZE, 10);
ObjectSetString(0, "MyLabel", OBJPROP_FONT, "Arial");

// ✅ CORRECT - Label (screen coordinates)
ObjectCreate(0, "InfoPanel", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "InfoPanel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "InfoPanel", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "InfoPanel", OBJPROP_YDISTANCE, 20);

// ❌ WRONG - Using non-existent properties
ObjectSetInteger(0, "MyObj", OBJPROP_ZORDER, 1);  // Doesn't exist in MQL4
ObjectSetInteger(0, "MyObj", OBJPROP_HEIGHT, 100);  // Use OBJPROP_YSIZE

Common Object Types

OBJ_RECTANGLE      // Rectangle object
OBJ_TREND          // Trend line
OBJ_HLINE          // Horizontal line
OBJ_VLINE          // Vertical line
OBJ_TEXT           // Text at chart coordinates
OBJ_LABEL          // Text at screen coordinates
OBJ_ARROW          // Arrow marker

Line Styles

STYLE_SOLID        // Solid line
STYLE_DASH         // Dashed line
STYLE_DOT          // Dotted line
STYLE_DASHDOT      // Dash-dot line
STYLE_DASHDOTDOT   // Dash-dot-dot line

6. String Operations and Formatting

Rule

Use correct string functions for MQL4
Be careful with string concatenation and formatting

Examples

// ✅ CORRECT - String concatenation
string message = "Value: " + DoubleToString(value, 2);
string info = "Ticket: " + IntegerToString(ticket);
string combined = part1 + " | " + part2 + " | " + part3;

// ✅ CORRECT - Number to string conversion
string priceStr = DoubleToString(price, Digits);
string lotsStr = DoubleToString(lots, 2);
string countStr = IntegerToString(count);

// ✅ CORRECT - String functions
int length = StringLen(myString);
string substr = StringSubstr(myString, 0, 10);
int pos = StringFind(myString, "pattern");
string upper = StringToUpper(myString);
string lower = StringToLower(myString);

// ✅ CORRECT - String filling
string spaces = "";
int i;
for(i = 0; i < 10; i++)
{
   spaces += " ";
}

// ❌ WRONG - Using undefined functions
string formatted = StringFormat("%d", value);  // Use DoubleToString instead

7. Time and Date Functions

Rule

Use MQL4 time functions correctly
Be aware of server time vs local time

Examples

// ✅ CORRECT - Time functions
datetime serverTime = TimeCurrent();      // Broker server time
datetime localTime = TimeLocal();         // Computer local time
datetime gmtTime = TimeGMT();             // GMT time

// ✅ CORRECT - Time components
int year = TimeYear(serverTime);
int month = TimeMonth(serverTime);
int day = TimeDay(serverTime);
int hour = TimeHour(serverTime);
int minute = TimeMinute(serverTime);
int dayOfWeek = TimeDayOfWeek(serverTime);  // 0=Sunday, 1=Monday, etc.

// ✅ CORRECT - Bar time
datetime barTime = iTime(Symbol(), PERIOD_H4, 0);
datetime prevBarTime = iTime(Symbol(), PERIOD_H4, 1);

// ✅ CORRECT - Time string formatting
string timeStr = TimeToString(serverTime, TIME_DATE|TIME_MINUTES|TIME_SECONDS);

8. Account and Trade Information

Rule

Use account info functions, not AccountInfo variants*
Select orders before accessing order properties

Examples

// ✅ CORRECT - Account information (MQL4 style)
double balance = AccountBalance();
double equity = AccountEquity();
double freeMargin = AccountFreeMargin();
double margin = AccountMargin();
double profit = AccountProfit();
int leverage = AccountLeverage();
string currency = AccountCurrency();

// ✅ CORRECT - Order selection and properties
int total = OrdersTotal();
int i;
for(i = 0; i < total; i++)
{
   if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
   {
      if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber)
      {
         int ticket = OrderTicket();
         int type = OrderType();
         double openPrice = OrderOpenPrice();
         double sl = OrderStopLoss();
         double tp = OrderTakeProfit();
         double orderLots = OrderLots();
         datetime openTime = OrderOpenTime();
         double orderProfit = OrderProfit();
         string comment = OrderComment();
      }
   }
}

// ❌ WRONG - Using MQL5 style functions
double balance = AccountInfoDouble(ACCOUNT_BALANCE);  // This is MQL5

9. Global Variables vs Input Parameters

Rule

Use proper input parameter declaration
Understand the difference between input, extern, and global variables

Examples

// ✅ CORRECT - Input parameters (user can modify)
input double   InpLotSize = 0.01;          // Lot size
input int      InpStopLossPips = 30;       // Stop loss in pips
input int      InpMagicNumber = 123456;    // Magic number
input color    InpBuyColor = clrGreen;     // Buy zone color
input bool     InpEnableTrading = true;    // Enable trading

// ✅ CORRECT - Global variables (internal state)
datetime g_lastBarTime = 0;
int g_totalOrders = 0;
bool g_initialized = false;

// ✅ CORRECT - Constants
#define MAX_ZONES 100
#define EA_VERSION "1.00"

// ❌ WRONG - Mixing input with complex initialization
input datetime g_startTime = TimeCurrent();  // Can't use functions in input

10. Common Pitfalls and Best Practices

OnTick() Execution Control

// ✅ CORRECT - Execute once per bar
datetime g_lastBarTime = 0;

void OnTick()
{
   datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0);
   
   if(currentBarTime != g_lastBarTime)
   {
      g_lastBarTime = currentBarTime;
      
      // Execute trading logic here (once per new bar)
      CheckForSignals();
   }
   
   // Monitoring logic that runs every tick
   MonitorOpenPositions();
}

Risk Management

// ✅ CORRECT - Dynamic lot sizing based on risk
double CalculateLotSize(double riskPercent, double slPips)
{
   double accountEquity = AccountEquity();
   double riskAmount = accountEquity * (riskPercent / 100.0);
   
   double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
   double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
   double point = MarketInfo(Symbol(), MODE_POINT);
   
   // Calculate pip value (for 4/5 digit brokers)
   double pipValue = tickValue;
   if(Digits == 3 || Digits == 5)
      pipValue = tickValue * 10;
   
   // Calculate lot size
   double lots = riskAmount / (slPips * pipValue);
   
   // Normalize to broker requirements
   return NormalizeLots(lots);
}

// ❌ WRONG - Hardcoded lot size
double lots = 0.01;  // No risk management!

Error Description Function

// ✅ CORRECT - Comprehensive error handling
string ErrorDescription(int errorCode)
{
   switch(errorCode)
   {
      case 0:   return "No error";
      case 1:   return "No error, result unknown";
      case 2:   return "Common error";
      case 4:   return "Trade server busy";
      case 6:   return "No connection";
      case 8:   return "Too frequent requests";
      case 129: return "Invalid price";
      case 130: return "Invalid stops";
      case 131: return "Invalid volume";
      case 132: return "Market closed";
      case 133: return "Trading disabled";
      case 134: return "Not enough money";
      case 135: return "Price changed";
      case 136: return "Off quotes";
      case 138: return "Requote";
      case 146: return "Trade context busy";
      default:  return "Unknown error";
   }
}

11. Compilation and Testing

Pre-Compilation Checklist

  • All loop variables declared before for-loops
  • All prices normalized to Digits precision
  • All lot sizes normalized to broker requirements
  • All trade operations have error handling
  • All object property constants verified
  • No MQL5-specific functions used
  • Input parameters properly declared
  • No functions called in input initialization

Testing Checklist

  • Test on demo account first
  • Verify order placement works correctly
  • Check zone drawing appears correctly
  • Validate risk management calculations
  • Test with different broker conditions
  • Verify behavior across timeframe changes
  • Check for memory leaks (objects, indicators)
  • Test deinitialization cleanup

12. Key Differences: MQL4 vs MQL5

Feature MQL4 MQL5
Pointer Access Use . operator Use . operator (NOT ->)
Loop Variables Declare before loop Declare before loop
Trade Functions OrderSend(), OrderModify() CTrade class
Account Info AccountBalance() AccountInfoDouble()
Order Selection OrderSelect() required PositionSelect() different
Magic Number Parameter in OrderSend() Set via CTrade::SetExpertMagicNumber()
Indicator Handles Direct calls (iMA() returns value) Returns handle, use CopyBuffer()
Object Management ObjectCreate(), ObjectSetInteger() Same, but some properties differ

Summary

Critical Rules for MQL4:

  1. Declare loop variables BEFORE for-loop
  2. Use dot operator (.) only
  3. Normalize all prices and lot sizes
  4. Always check trade function return values
  5. Verify object property constant names
  6. Use MQL4 account functions, not MQL5 variants
  7. Implement proper error handling with GetLastError()
  8. Execute trading logic once per bar
  9. Calculate position size dynamically based on risk
  10. Test thoroughly on demo before live trading

Following these rules will prevent 95% of common MQL4 compilation and runtime errors.