Problem Description
Solution
To capture the price of every single tick in MQL4 for a fast scalping strategy, you must utilize the OnTick() event handler combined with the SymbolInfoTick() function.
In high-frequency or fast scalping strategies, using the standard Bid and Ask variables is acceptable, but SymbolInfoTick() is superior because it populates an MqlTick structure. This ensures that the Bid, Ask, and Time values are synchronized from the exact same snapshot, preventing data mismatch during highly volatile moments.
Key Implementation Details
- Event Driven: The code must reside inside
OnTick(). This function triggers every time the server sends a new price update. - Atomic Data: Use
MqlTickto get Bid and Ask simultaneously. - Speed Optimization: To ensure you process "every" tick, your code inside
OnTickmust execute faster than the arrival of the next tick. AvoidSleep(), heavy loops, or complex graphical object creation inside the main trading logic.
MQL4 Scalping Template
Here is a complete, optimized framework for a tick-based scalping strategy.
//+------------------------------------------------------------------+
//| FastTickScalper.mq4 |
//| Copyright 2023, Quantitative AI Assistant |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI Assistant"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input double InpLots = 0.1; // Lot size
input int InpStopLoss = 10; // Stop Loss in points
input int InpTakeProfit = 10; // Take Profit in points
input int InpMaxSpread = 15; // Max allowed spread in points
input int InpSlippage = 3; // Max slippage in points
input int InpMagicNum = 12345; // Magic Number
//--- Global Variables for Tick Analysis
MqlTick currentTick;
double lastTickBid = 0;
double lastTickAsk = 0;
double point;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize point value for calculations
point = Point;
if(Digits == 3 || Digits == 5) point *= 10;
// Initial data population
if(!SymbolInfoTick(_Symbol, currentTick))
{
Print("Failed to get initial tick data");
return(INIT_FAILED);
}
lastTickBid = currentTick.bid;
lastTickAsk = currentTick.ask;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. GET THE TICK DATA
// We use SymbolInfoTick for atomic data access (Bid/Ask/Time sync)
if(!SymbolInfoTick(_Symbol, currentTick))
{
// If we can't get the tick, we can't trade. Exit immediately.
return;
}
// 2. CHECK SPREAD (Crucial for Scalping)
// Calculate spread in points
int spread = (int)((currentTick.ask - currentTick.bid) / Point);
// If spread is too high, do not execute logic
if(spread > InpMaxSpread)
{
// Update history for next comparison and exit
lastTickBid = currentTick.bid;
lastTickAsk = currentTick.ask;
return;
}
// 3. FAST SCALPING LOGIC
// Example Logic: Detect a micro-momentum (Tick moves up/down)
// NOTE: Replace this block with your specific algo logic
bool signalBuy = false;
bool signalSell = false;
// Simple example: If Bid moved up significantly in one tick
if(currentTick.bid > lastTickBid + point)
{
signalBuy = true;
}
// Simple example: If Bid moved down significantly in one tick
else if(currentTick.bid < lastTickBid - point)
{
signalSell = true;
}
// 4. EXECUTION
// Only check orders if we have a signal to save CPU cycles
if(signalBuy || signalSell)
{
// Check if we already have open positions to avoid over-trading
if(OrdersTotal() == 0)
{
if(signalBuy)
{
OpenTrade(OP_BUY, currentTick.ask);
}
else if(signalSell)
{
OpenTrade(OP_SELL, currentTick.bid);
}
}
}
// 5. STORE HISTORY
// Store current tick as "last" for the next execution
lastTickBid = currentTick.bid;
lastTickAsk = currentTick.ask;
}
//+------------------------------------------------------------------+
//| Helper: Open Trade |
//+------------------------------------------------------------------+
void OpenTrade(int type, double price)
{
double sl = 0, tp = 0;
// Calculate SL/TP based on direction
if(type == OP_BUY)
{
if(InpStopLoss > 0) sl = price - InpStopLoss * point;
if(InpTakeProfit > 0) tp = price + InpTakeProfit * point;
}
else if(type == OP_SELL)
{
if(InpStopLoss > 0) sl = price + InpStopLoss * point;
if(InpTakeProfit > 0) tp = price - InpTakeProfit * point;
}
// Normalize prices
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
price = NormalizeDouble(price, Digits);
// Send Order
int ticket = OrderSend(_Symbol, type, InpLots, price, InpSlippage, sl, tp, "FastScalper", InpMagicNum, 0, clrNONE);
if(ticket < 0)
{
Print("OrderSend failed with error #", GetLastError());
}
}
Code Explanation
MqlTick currentTick: This structure is the core of the strategy. It holdstime,bid,ask,last, andvolume. Using this structure is faster and cleaner than callingMarketInfo()multiple times.SymbolInfoTick(_Symbol, currentTick): This function fills the structure with the very latest data from the market watch. It returnstrueif successful.- Spread Filter: Scalping relies on tight spreads. The code calculates the spread immediately and exits
OnTickif the spread exceedsInpMaxSpread. This prevents the EA from calculating logic during expensive market conditions. - Logic Execution: The logic compares the
currentTickagainstlastTickBid(stored from the previous execution). This allows you to analyze price velocity or direction tick-by-tick. - Efficiency: The code checks
OrdersTotal()only after a signal is generated. This saves processing time on ticks where no signal exists, which is the majority of the time.
Limitations of MQL4 for Tick Scalping
While the code above is optimized, you must be aware of the platform limitations:
- Single Threading: MQL4 EAs run on a single thread. If your
OnTick()logic takes 100ms to execute, but 3 ticks arrive during that 100ms, you will miss 2 ticks. The EA will only process the latest tick available when it finishes the previous calculation. - No Tick History: Unlike MQL5, MQL4 does not have a native
CopyTicksfunction to retrieve history. You can only see the current tick. If you miss a tick because the CPU was busy, that data is lost to the EA. - Latency: Network latency (ping) is often higher than the time between ticks during news events. A VPS located near the broker's server is mandatory for tick scalping.
Q&A
Q: Can I backtest this tick strategy in MT4?
A: Yes, but with limitations. MT4 Strategy Tester generates ticks based on M1 OHLC data (interpolation), meaning the ticks are simulated, not real. For accurate tick scalping backtests, you must use "Tick Data Suite" or similar 3rd party tools to inject real tick history into MT4, or switch to MT5 which supports real tick data natively.
Q: How do I make the code run faster?
A: Remove any Print() or Comment() functions from the OnTick loop, as writing to the log/screen is slow. Ensure you are not calling heavy custom indicators (iCustom) on every tick; instead, code the indicator logic directly into the EA if possible.
Q: Why use SymbolInfoTick instead of Bid and Ask?
A: Bid and Ask are variables that are updated automatically, but SymbolInfoTick fills a structure that includes the time and volume of that specific tick update. It ensures that if you read the Bid and then the Ask, they belong to the exact same market snapshot.