- 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
545 lines
16 KiB
Markdown
545 lines
16 KiB
Markdown
---
|
|
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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
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
|
|
```mql4
|
|
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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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
|
|
```mql4
|
|
// ✅ 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.
|