- 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
776 lines
22 KiB
Markdown
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.
|