MQL4/.github/instructions/mql4-experts.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

776 lines
22 KiB
Markdown

---
applyTo: "**/MQL4/Experts/_Thivyam/**/*.mq4"
---
# Instructions for _Thivyam MQL4 Expert Advisors
## CRITICAL: MQL4 Syntax Compliance
**Before writing any MQL4 code, read and follow:** `mql4-syntax-critical.instructions.md`
Key mandatory rules:
- Declare loop variables BEFORE for-loop: `int i; for(i=0; i<n; i++)`
- Use dot operator (`.`) for all member access
- Always use `GetLastError()` after trade operations
- Normalize all prices with `NormalizeDouble(price, Digits)`
- Normalize lot sizes to broker requirements
- Use `OrderSelect()` before accessing order properties
---
## EA Structure and Organization
### File Organization
```mql4
//+------------------------------------------------------------------+
//| YourStrategyName.mq4 |
//| Author: Your Name |
//| https://www.yourwebsite.com |
//| Version: 1.00 |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link "https://www.yourwebsite.com"
#property version "1.00"
#property strict
#property description "Brief description of strategy"
#property description "Additional details or requirements"
// INPUT PARAMETERS
input double InpLotSize = 0.01;
input int InpStopLossPips = 30;
// ... more inputs
// GLOBAL CONSTANTS
#define MAX_ZONES 100
// GLOBAL VARIABLES
datetime g_lastBarTime = 0;
int g_zoneCount = 0;
// ... more globals
// FUNCTION DECLARATIONS (optional but recommended for organization)
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialization code
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Cleanup code
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Main trading logic
}
// HELPER FUNCTIONS
// ... all your helper functions
```
---
## Initialization (`OnInit()`)
### Required Initialization Steps
```mql4
int OnInit()
{
// 1. Validate input parameters
if(InpLotSize <= 0)
{
Print("ERROR: LotSize must be greater than 0");
return INIT_PARAMETERS_INCORRECT;
}
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(InpLotSize < minLot || InpLotSize > maxLot)
{
Print("ERROR: LotSize must be between ", minLot, " and ", maxLot);
return INIT_PARAMETERS_INCORRECT;
}
// 2. Check trading permissions
if(!IsTradeAllowed())
{
Print("ERROR: Trading is not allowed. Enable AutoTrading.");
return INIT_FAILED;
}
// 3. Validate unique magic number
if(InpMagicNumber == 0)
{
Print("WARNING: Magic number is 0. Consider using a unique value.");
}
// 4. Initialize arrays and data structures
InitializeArrays();
// 5. Initialize bar time tracker
g_lastBarTime = iTime(Symbol(), PERIOD_CURRENT, 0);
// 6. Create visual elements (optional)
CreateInfoPanel();
// 7. Log initialization success
Print("========================================");
Print("EA Initialized Successfully");
Print("Symbol: ", Symbol());
Print("Magic Number: ", InpMagicNumber);
Print("Lot Size: ", DoubleToString(InpLotSize, 2));
Print("========================================");
return INIT_SUCCEEDED;
}
```
### Return Codes
- `INIT_SUCCEEDED` - Initialization successful, EA will run
- `INIT_FAILED` - Initialization failed, EA will not run
- `INIT_PARAMETERS_INCORRECT` - Invalid input parameters
---
## Deinitialization (`OnDeinit()`)
### Required Cleanup Steps
```mql4
void OnDeinit(const int reason)
{
// 1. Log deinitialization reason
Print("========================================");
Print("EA Deinitialization - Reason: ", reason);
Print("========================================");
// 2. Delete all chart objects created by this EA
DeleteAllChartObjects();
// 3. Clear chart comment
Comment("");
// 4. Optional: Close all open positions (usually commented out)
// CloseAllOrders();
}
```
### Deinitialization Reasons
```mql4
// Common reason codes:
// 0 - EA removed from chart
// 1 - Program recompiled
// 2 - Symbol or timeframe changed
// 3 - Chart closed
// 5 - Input parameters changed
```
---
## Main Trading Logic (`OnTick()`)
### Structure: Once-Per-Bar Execution
```mql4
datetime g_lastBarTime = 0;
void OnTick()
{
// 1. Check for new bar
datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0);
bool newBar = (currentBarTime != g_lastBarTime);
if(newBar)
{
g_lastBarTime = currentBarTime;
// 2. Execute once-per-bar logic
CheckForEntrySignals();
UpdateZones();
ValidateState();
}
// 3. Execute every-tick logic
MonitorOpenPositions();
MonitorPendingOrders();
// 4. Update display
UpdateInfoPanel();
}
```
### Multi-Timeframe Execution
```mql4
datetime g_lastH4BarTime = 0;
datetime g_lastM30BarTime = 0;
datetime g_lastM15BarTime = 0;
void OnTick()
{
// Check for new bars on each timeframe
bool newH4Bar = IsNewBar(PERIOD_H4);
bool newM30Bar = IsNewBar(PERIOD_M30);
bool newM15Bar = IsNewBar(PERIOD_M15);
// Process each timeframe
if(newH4Bar) ProcessH4();
if(newM30Bar) ProcessM30();
if(newM15Bar) ProcessM15();
// Continuous monitoring
MonitorAllZones();
UpdateDisplay();
}
bool IsNewBar(int period)
{
datetime currentBarTime = iTime(Symbol(), period, 0);
datetime lastBarTime = 0;
if(period == PERIOD_H4) lastBarTime = g_lastH4BarTime;
else if(period == PERIOD_M30) lastBarTime = g_lastM30BarTime;
else if(period == PERIOD_M15) lastBarTime = g_lastM15BarTime;
if(currentBarTime != lastBarTime)
{
if(period == PERIOD_H4) g_lastH4BarTime = currentBarTime;
else if(period == PERIOD_M30) g_lastM30BarTime = currentBarTime;
else if(period == PERIOD_M15) g_lastM15BarTime = currentBarTime;
return true;
}
return false;
}
```
---
## Trade Execution Best Practices
### Order Placement Template
```mql4
int PlacePendingOrder(string type, double entry, double sl, double tp, string comment)
{
// 1. Determine order type
int orderType;
color arrowColor;
if(type == "BuyLimit")
{
orderType = OP_BUYLIMIT;
arrowColor = clrGreen;
}
else if(type == "SellLimit")
{
orderType = OP_SELLLIMIT;
arrowColor = clrRed;
}
// ... other types
// 2. Validate and normalize prices
entry = NormalizeDouble(entry, Digits);
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
if(!IsPriceValid(entry) || !IsPriceValid(sl) || !IsPriceValid(tp))
{
Print("ERROR: Invalid price levels");
return -1;
}
// 3. Check stop level requirements
double minStopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
double slDistance = MathAbs(entry - sl);
if(slDistance < minStopLevel)
{
Print("ERROR: SL too close to entry. Min: ", minStopLevel, " Current: ", slDistance);
return -1;
}
// 4. Normalize lot size
double lots = NormalizeLots(InpLotSize);
// 5. Check margin
double requiredMargin = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * lots;
double freeMargin = AccountFreeMargin();
if(freeMargin < requiredMargin)
{
Print("ERROR: Insufficient margin. Required: ", requiredMargin, " Available: ", freeMargin);
return -1;
}
// 6. Place the order
int ticket = OrderSend(Symbol(), orderType, lots, entry, InpSlippage,
sl, tp, comment, InpMagicNumber, 0, arrowColor);
// 7. Handle result
if(ticket > 0)
{
Print("Order placed successfully. Ticket: ", ticket, " Type: ", type,
" Entry: ", entry, " SL: ", sl, " TP: ", tp);
return ticket;
}
else
{
int error = GetLastError();
Print("OrderSend failed. Error: ", error, " - ", ErrorDescription(error));
return -1;
}
}
```
### Position Monitoring Template
```mql4
void MonitorOpenPositions()
{
int total = OrdersTotal();
int i;
for(i = total - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
// Filter by symbol and magic number
if(OrderSymbol() != Symbol()) continue;
if(OrderMagicNumber() != InpMagicNumber) continue;
// Only process market positions (not pending orders)
if(OrderType() != OP_BUY && OrderType() != OP_SELL) continue;
// Get position details
int ticket = OrderTicket();
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
double currentTP = OrderTakeProfit();
double profit = OrderProfit();
// Implement trailing stop, break-even, etc.
if(OrderType() == OP_BUY)
{
double newSL = CalculateTrailingStop(OP_BUY, openPrice, currentSL);
if(newSL > currentSL && newSL < Bid - MarketInfo(Symbol(), MODE_STOPLEVEL) * Point)
{
bool modified = OrderModify(ticket, openPrice, newSL, currentTP, 0, clrBlue);
if(!modified)
{
Print("OrderModify failed: ", GetLastError());
}
}
}
else if(OrderType() == OP_SELL)
{
double newSL = CalculateTrailingStop(OP_SELL, openPrice, currentSL);
if((newSL < currentSL || currentSL == 0) && newSL > Ask + MarketInfo(Symbol(), MODE_STOPLEVEL) * Point)
{
bool modified = OrderModify(ticket, openPrice, newSL, currentTP, 0, clrRed);
if(!modified)
{
Print("OrderModify failed: ", GetLastError());
}
}
}
}
}
```
---
## Risk Management (MANDATORY)
### Dynamic Lot Sizing
```mql4
// NEVER use hardcoded lot size!
// ALWAYS calculate based on risk percentage and stop loss distance
double CalculateLotSize(double riskPercent, double slPips)
{
// Get account equity
double equity = AccountEquity();
// Calculate risk amount in account currency
double riskAmount = equity * (riskPercent / 100.0);
// Get tick value and size
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double point = MarketInfo(Symbol(), MODE_POINT);
// Calculate pip value (handle 4/5 digit brokers)
double pipValue = tickValue;
if(Digits == 3 || Digits == 5)
{
pipValue = tickValue * 10;
}
// Calculate lot size
double lots = 0;
if(slPips > 0 && pipValue > 0)
{
lots = riskAmount / (slPips * pipValue);
}
// Normalize to broker requirements
return NormalizeLots(lots);
}
double NormalizeLots(double lots)
{
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
// Round to lot step
lots = MathFloor(lots / lotStep) * lotStep;
// Enforce limits
lots = MathMax(lots, minLot);
lots = MathMin(lots, maxLot);
return NormalizeDouble(lots, 2);
}
```
### Usage Example
```mql4
// User input for risk
input double InpRiskPercent = 1.0; // Risk per trade (%)
void PlaceTradeWithRiskManagement()
{
// Calculate SL distance in pips
double slPips = 30; // Or calculate from ATR, zone size, etc.
// Calculate position size
double lots = CalculateLotSize(InpRiskPercent, slPips);
if(lots <= 0)
{
Print("ERROR: Cannot calculate valid lot size");
return;
}
// Place order with calculated lot size
int ticket = PlacePendingOrder("BuyLimit", entry, sl, tp, "Risk: " + DoubleToString(InpRiskPercent, 1) + "%");
}
```
---
## Pattern Detection Best Practices
### Bullish Engulfing Detection
```mql4
bool IsRegularBuy(int timeframe, int index)
{
// Pattern: Bearish candle followed by Bullish candle that closes above bearish high
// Index points to the bullish (engulfing) candle
// index+1 is the bearish (anchor) candle
double anchor_open = iOpen(Symbol(), timeframe, index+1);
double anchor_close = iClose(Symbol(), timeframe, index+1);
double anchor_high = iHigh(Symbol(), timeframe, index+1);
double anchor_low = iLow(Symbol(), timeframe, index+1);
double current_open = iOpen(Symbol(), timeframe, index);
double current_close = iClose(Symbol(), timeframe, index);
double current_high = iHigh(Symbol(), timeframe, index);
double current_low = iLow(Symbol(), timeframe, index);
// Check if anchor is bearish
if(anchor_open <= anchor_close) return false;
// Check if current is bullish
if(current_open >= current_close) return false;
// Check if current closes above anchor high (engulfing)
if(current_close > anchor_high) return true;
return false;
}
```
### Bearish Engulfing Detection
```mql4
bool IsRegularSell(int timeframe, int index)
{
// Pattern: Bullish candle followed by Bearish candle that closes below bullish low
double anchor_open = iOpen(Symbol(), timeframe, index+1);
double anchor_close = iClose(Symbol(), timeframe, index+1);
double anchor_high = iHigh(Symbol(), timeframe, index+1);
double anchor_low = iLow(Symbol(), timeframe, index+1);
double current_open = iOpen(Symbol(), timeframe, index);
double current_close = iClose(Symbol(), timeframe, index);
double current_high = iHigh(Symbol(), timeframe, index);
double current_low = iLow(Symbol(), timeframe, index);
// Check if anchor is bullish
if(anchor_open >= anchor_close) return false;
// Check if current is bearish
if(current_open <= current_close) return false;
// Check if current closes below anchor low (engulfing)
if(current_close < anchor_low) return true;
return false;
}
```
---
## Zone Management Best Practices
### Zone Storage Using Arrays
```mql4
// Define maximum zones
#define MAX_H4_ZONES 50
#define MAX_M30_ZONES 100
#define MAX_M15_ZONES 200
// H4 Zone arrays
double h4_ZoneHighs[MAX_H4_ZONES];
double h4_ZoneLows[MAX_H4_ZONES];
datetime h4_ZoneTimes[MAX_H4_ZONES];
string h4_ZoneTypes[MAX_H4_ZONES]; // "Buy" or "Sell"
bool h4_ZoneActive[MAX_H4_ZONES];
int h4_ZoneCount = 0;
// Initialize arrays
void InitializeArrays()
{
int i;
for(i = 0; i < MAX_H4_ZONES; i++)
{
h4_ZoneHighs[i] = 0;
h4_ZoneLows[i] = 0;
h4_ZoneTimes[i] = 0;
h4_ZoneTypes[i] = "";
h4_ZoneActive[i] = false;
}
h4_ZoneCount = 0;
}
// Create zone
void CreateH4Zone(string type, int barIndex)
{
if(h4_ZoneCount >= MAX_H4_ZONES)
{
Print("ERROR: Maximum H4 zones reached");
return;
}
double high = iHigh(Symbol(), PERIOD_H4, barIndex);
double low = iLow(Symbol(), PERIOD_H4, barIndex);
datetime time = iTime(Symbol(), PERIOD_H4, barIndex);
h4_ZoneHighs[h4_ZoneCount] = high;
h4_ZoneLows[h4_ZoneCount] = low;
h4_ZoneTimes[h4_ZoneCount] = time;
h4_ZoneTypes[h4_ZoneCount] = type;
h4_ZoneActive[h4_ZoneCount] = true;
// Draw zone on chart
DrawH4Zone(h4_ZoneCount);
h4_ZoneCount++;
}
```
### Zone Drawing
```mql4
void DrawH4Zone(int zoneIndex)
{
string objName = "H4_Zone_" + IntegerToString(zoneIndex);
string labelName = "H4_Label_" + IntegerToString(zoneIndex);
datetime time1 = h4_ZoneTimes[zoneIndex];
datetime time2 = time1 + (PERIOD_H4 * 60 * 100); // Extend forward
color zoneColor = (h4_ZoneTypes[zoneIndex] == "Buy") ? clrGreen : clrRed;
// Delete if exists
ObjectDelete(0, objName);
ObjectDelete(0, labelName);
// Create rectangle
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, time1, h4_ZoneHighs[zoneIndex], time2, h4_ZoneLows[zoneIndex]);
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, objName, OBJPROP_BACK, false);
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, objName, OBJPROP_RAY_RIGHT, true);
// Create label
string labelText = "H4 " + h4_ZoneTypes[zoneIndex];
double labelPrice = (h4_ZoneHighs[zoneIndex] + h4_ZoneLows[zoneIndex]) / 2.0;
ObjectCreate(0, labelName, OBJ_TEXT, 0, time1, labelPrice);
ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, zoneColor);
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8);
ObjectSetString(0, labelName, OBJPROP_FONT, "Arial Bold");
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
}
```
---
## Error Handling (MANDATORY)
### Comprehensive Error Handler
```mql4
string ErrorDescription(int errorCode)
{
switch(errorCode)
{
case 0: return "No error";
case 1: return "No error, result unknown";
case 2: return "Common error";
case 3: return "Invalid trade parameters";
case 4: return "Trade server busy";
case 5: return "Old terminal version";
case 6: return "No connection with trade server";
case 7: return "Not enough rights";
case 8: return "Too frequent requests";
case 64: return "Account disabled";
case 65: return "Invalid account";
case 128: return "Trade timeout";
case 129: return "Invalid price";
case 130: return "Invalid stops (too close to market)";
case 131: return "Invalid trade volume";
case 132: return "Market is closed";
case 133: return "Trading is disabled";
case 134: return "Not enough money";
case 135: return "Price changed (requote)";
case 136: return "Off quotes";
case 138: return "Requote";
case 139: return "Order is locked";
case 141: return "Too many requests";
case 145: return "Modification denied (too close to market)";
case 146: return "Trade context is busy";
case 147: return "Expirations denied by broker";
case 148: return "Too many open orders";
default: return "Unknown error";
}
}
```
---
## Visual Display Best Practices
### Info Panel Template
```mql4
void CreateInfoPanel()
{
string objName = "EA_InfoPanel";
ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 20);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 9);
ObjectSetString(0, objName, OBJPROP_FONT, "Courier New");
}
void UpdateInfoPanel()
{
string objName = "EA_InfoPanel";
string text = "";
text += "┌─ EA Name v1.00 ───────────┐\n";
text += "│ Symbol: " + Symbol() + "\n";
text += "│ Balance: " + DoubleToString(AccountBalance(), 2) + "\n";
text += "│ Equity: " + DoubleToString(AccountEquity(), 2) + "\n";
text += "│ Zones: " + IntegerToString(CountActiveZones()) + "\n";
text += "│ Orders: " + IntegerToString(CountOpenOrders()) + "\n";
text += "└───────────────────────────┘";
ObjectSetString(0, objName, OBJPROP_TEXT, text);
}
```
---
## Testing and Validation
### Pre-Deployment Checklist
- [ ] All input parameters validated in `OnInit()`
- [ ] Risk management implemented (no hardcoded lot sizes)
- [ ] All trade operations wrapped with error handling
- [ ] Once-per-bar execution enforced
- [ ] All prices and lots normalized
- [ ] Stop level requirements checked
- [ ] Margin requirements validated before trading
- [ ] All chart objects cleaned up in `OnDeinit()`
- [ ] Magic number used to filter orders
- [ ] Strategy Tester passes without errors
- [ ] Demo account testing completed successfully
### Common Issues to Check
1. **Error 130 (Invalid Stops)**: Check `MODE_STOPLEVEL` compliance
2. **Error 131 (Invalid Volume)**: Verify lot normalization
3. **Error 134 (Not Enough Money)**: Check margin before trading
4. **Multiple orders on same bar**: Implement new bar detection
5. **Orders not closing**: Verify `OrderSelect()` and `OrderClose()` logic
6. **Zone objects not displaying**: Check object property constants
7. **Memory leaks**: Ensure all objects deleted in `OnDeinit()`
---
## Demo/Trial Version Implementation
### Time-Limited Demo
```mql4
int OnInit()
{
// Demo expiry check
datetime expiryDate = D'2025.12.31 23:59:59';
datetime currentDate = TimeCurrent();
if(currentDate >= expiryDate)
{
Alert("DEMO VERSION EXPIRED!");
Print("ERROR: Demo period has ended. Contact vendor for full version.");
Comment("Demo Version Expired\nContact vendor for full version");
return INIT_FAILED;
}
// Log days remaining
int daysRemaining = (int)((expiryDate - currentDate) / 86400);
Print("Demo Period: ", daysRemaining, " days remaining until ", TimeToString(expiryDate, TIME_DATE));
// Rest of initialization...
return INIT_SUCCEEDED;
}
```
---
## Summary: MQL4 EA Best Practices
1.**Syntax Compliance**: Follow mql4-syntax-critical.instructions.md
2.**Input Validation**: Validate all user inputs in `OnInit()`
3.**Risk Management**: Calculate lot size dynamically based on risk %
4.**Error Handling**: Use `GetLastError()` after all trade operations
5.**Execution Control**: Execute trading logic once per bar
6.**Normalization**: Normalize all prices and lot sizes
7.**Margin Check**: Verify sufficient margin before placing orders
8.**Magic Number**: Use unique magic number to filter orders
9.**Resource Cleanup**: Delete all objects in `OnDeinit()`
10.**Testing**: Test thoroughly on demo before live deployment
Following these practices ensures robust, reliable, and professional MQL4 Expert Advisors.