Generate code in 3 minutes
Enter your idea and AI generates runnable code.
To optimize a complex MQL4 trading algorithm for speed and memory efficiency, you must minimize redundant calculations, manage dynamic memory explicitly, and optimize how you access market history.
Here are the specific strategies and a code template to achieve this.
Bars or OrdersHistoryTotal() entirely on every tick. Store the last processed time or index and only process new data.iCustom calls are expensive. Do not call them inside loops if the parameters don't change. Calculate the value once per bar and store it in a variable.Comment(), ChartRedraw(), and Object functions are slow because they interact with the terminal's GUI thread. Update them only when necessary (e.g., on a new bar or via a Timer).double arr[]), frequent resizing is memory-intensive. Use ArrayResize(arr, size, reserve) to allocate extra memory blocks in advance to prevent memory fragmentation.ArrayFree() or delete (for class objects) when data is no longer needed.iOpen, iHigh, etc., for deep history (e.g., index 50,000) forces the terminal to load history files into RAM. Limit your lookback period to what is strictly necessary.This template demonstrates a "New Bar" event handler, efficient array memory reservation, and optimized order looping.
//+------------------------------------------------------------------+
//| OptimizedAlgorithm.mq4 |
//| Copyright 2023, Quantitative Trading AI |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int MagicNumber = 12345;
input int LookBackPeriod = 100; // Limit history processing
//--- Global Variables for Optimization
datetime LastBarTime = 0;
double MyDataArray[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Pre-allocate memory to avoid fragmentation during runtime
// We allocate 1000 slots, and reserve 1000 more to prevent frequent resizing
ArrayResize(MyDataArray, 1000, 1000);
// Initialize random seed if using MathRand
MathSrand(GetTickCount());
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Explicitly free memory
ArrayFree(MyDataArray);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. CRITICAL: New Bar Check
// Most logic should only run once per bar to save CPU
if(IsNewBar())
{
// Run heavy calculations here
RunComplexLogic();
// Update visual comments only once per bar
Comment("Last calculation: ", TimeToString(TimeCurrent()));
}
// 2. Trade Management (Trailing stops, etc.)
// This might need to run every tick, but keep it lightweight
ManageOpenOrders();
}
//+------------------------------------------------------------------+
//| Check for a new bar opening |
//+------------------------------------------------------------------+
bool IsNewBar()
{
if(LastBarTime != Time[0])
{
LastBarTime = Time[0];
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| Heavy Calculation Logic |
//+------------------------------------------------------------------+
void RunComplexLogic()
{
// OPTIMIZATION: Only process the required lookback period
// Do NOT use Bars or iBars(Symbol(), Period()) in the loop limit
int limit = MathMin(LookBackPeriod, Bars - 1);
// Resize array only if strictly necessary
if(ArraySize(MyDataArray) < limit)
ArrayResize(MyDataArray, limit, 500); // Reserve 500 extra slots
for(int i = 0; i < limit; i++)
{
// Example: Simple Moving Average calculation
// Using iMA directly is faster than manual loops for standard indicators
MyDataArray[i] = iMA(NULL, 0, 14, 0, MODE_SMA, PRICE_CLOSE, i);
}
// Example: Check signal on the just closed bar (index 1)
if(MyDataArray[1] > MyDataArray[2])
{
// Signal Logic
}
}
//+------------------------------------------------------------------+
//| Efficient Order Management |
//+------------------------------------------------------------------+
void ManageOpenOrders()
{
// OPTIMIZATION: Loop backwards when dealing with orders
// (especially if deleting/closing) to avoid index shifting issues.
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
// Select by Position is faster than Ticket for general looping
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
// Filter early to save CPU cycles
if(OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
// Perform lightweight checks
if(OrderType() == OP_BUY)
{
// Trailing stop logic example
// ...
}
}
}
ArrayResize Reserve ParameterIn the code above, ArrayResize(MyDataArray, limit, 500) is used. The third parameter (500) is the reserve.
iCustom Memory LeaksIf you use iCustom, ensure you are not creating new handles (in MQL5) or causing excessive recalculations (in MQL4) inside loops.
iCustom inside a for loop that iterates 1000 times per tick.iCustom for specific indices (e.g., i and i+1) only when a new bar arrives.Print in OptimizationThe Print() function writes to the hard drive (log files). This is the single biggest bottleneck during Strategy Tester optimization.
// Use a macro to disable printing during optimization
if(!IsOptimization())
{
Print("Current Equity: ", AccountEquity());
}
Q: Does ArraySetAsSeries affect performance?
A: ArraySetAsSeries is very efficient. It does not physically move data in memory; it only changes the indexing logic (reverses the access order). It is preferred over ArrayCopy if you just need to read data backwards.
Q: Why is my backtest consuming gigabytes of RAM?
A: This usually happens if you access deep history (e.g., iClose(Symbol(), Period(), 100000)). MetaTrader 4 loads history data into RAM on demand. If your code requests bar #100,000, MT4 loads all 100,000 bars into memory. Restrict your loops to a defined LookBackPeriod.
Q: Is it faster to use built-in indicators (iMA) or calculate them manually in a loop?
A: Built-in functions (iMA, iRSI, etc.) are highly optimized in C++ within the terminal core. They are generally faster than writing the equivalent math loop in MQL4, unless you need a highly specific variation of the calculation.
Q: How do I profile my MQL4 code to find bottlenecks?
A: MQL4 does not have a built-in profiler like MQL5. You must use GetTickCount() or GetMicrosecondCount() to measure execution time manually.
ulong start = GetMicrosecondCount();
RunComplexLogic();
ulong end = GetMicrosecondCount();
Print("Logic took: " + IntegerToString(end - start) + " microseconds");