How to Build a Forex Trend Scanner Using 15-Minute and 30-Minute Charts
Last checked: 12 October 2026. This guide draws on the MQL5 reference for iMA, CopyBuffer and chart timeframes. Code is a teaching example; compile and test it on a demo account.
A trend scanner answers one question for several pairs at once: is price rising, falling or going nowhere? This article builds a simple scanner that checks the 15-minute and 30-minute charts for a list of symbols and prints the result in the Experts log. It is intentionally small, so you can see every decision.
Why use two timeframes
The 15-minute chart reacts faster, and the 30-minute chart is slower and calmer. When both point the same way, the trend reading is stronger than when they disagree. A scanner that reports “M15 up, M30 down” is telling you the market is in conflict, which is useful information, not a failure.
The trend rule
For each symbol and each timeframe, compare a fast and a slow exponential moving average, using the last closed bar:
- UP if the fast average is above the slow average by at least a small margin.
- DOWN if it is below by at least that margin.
- FLAT if the gap is smaller than the margin.
The margin is expressed as a fraction of the average true range, which adapts to each pair. A fixed number of points would treat a quiet pair and a volatile one the same way, which is misleading.

The scanner as a script
A script runs once and stops, which suits a scanner you start on demand. It can also open handles for other symbols, which is why this example uses a script instead of an indicator.
#property script_show_inputs
input string InpSymbols = "EURUSD,GBPUSD,USDJPY,AUDUSD,USDCAD";
input int InpFast = 20;
input int InpSlow = 50;
input int InpAtr = 14;
input double InpMarginAtr = 0.10; // gap must exceed 10% of ATR
string TrendState(const string sym, const ENUM_TIMEFRAMES tf)
{
int hF = iMA(sym, tf, InpFast, 0, MODE_EMA, PRICE_CLOSE);
int hS = iMA(sym, tf, InpSlow, 0, MODE_EMA, PRICE_CLOSE);
int hA = iATR(sym, tf, InpAtr);
if(hF == INVALID_HANDLE || hS == INVALID_HANDLE || hA == INVALID_HANDLE)
return("ERR");
double f[], s[], a[];
bool ok = (CopyBuffer(hF, 0, 1, 1, f) == 1 &&
CopyBuffer(hS, 0, 1, 1, s) == 1 &&
CopyBuffer(hA, 0, 1, 1, a) == 1);
IndicatorRelease(hF); IndicatorRelease(hS); IndicatorRelease(hA);
if(!ok) return("WAIT"); // history not loaded yet
double gap = f[0] - s[0];
if(gap > InpMarginAtr * a[0]) return("UP");
if(gap < -InpMarginAtr * a[0]) return("DOWN");
return("FLAT");
}
void OnStart()
{
string list[];
int n = StringSplit(InpSymbols, ',', list);
for(int i = 0; i < n; i++)
{
string sym = list[i];
StringTrimLeft(sym); StringTrimRight(sym);
if(!SymbolSelect(sym, true))
{ Print(sym, ": symbol not found at this broker"); continue; }
string m15 = TrendState(sym, PERIOD_M15);
string m30 = TrendState(sym, PERIOD_M30);
string verdict = (m15 == m30 && (m15 == "UP" || m15 == "DOWN"))
? "ALIGNED " + m15 : "MIXED";
PrintFormat("%-8s M15=%-5s M30=%-5s -> %s", sym, m15, m30, verdict);
}
}
Understanding the pieces
SymbolSelect makes sure the pair is in your Market Watch, because the terminal only holds data for symbols it knows about. Broker symbol names differ, and some add suffixes such as EURUSDm, so the list in the input may need editing. StringSplit turns the comma-separated input into an array.
The returned value “WAIT” matters. The first time the terminal sees a symbol and timeframe, the history may not be downloaded yet, and CopyBuffer then returns fewer values than requested. A scanner that treats missing data as “FLAT” would give false readings. Run the script twice if you see WAIT.
Releasing the handles at the end of each call is good housekeeping. If a scanner creates many handles and never releases them, the terminal keeps all those indicators alive.
Reading the output
- Open the Toolbox, then the Experts tab, to see the printed lines.
- ALIGNED UP or ALIGNED DOWN means both timeframes agree.
- MIXED means at least one is flat or they disagree. Do not force a decision on those pairs.
Improving it later
- Show results on the chart with labels instead of the log (the dashboard article covers this).
- Run it automatically on a timer inside an Expert Advisor.
- Add a higher-timeframe filter such as H4 (covered in a later article).
Step by step: running the scanner
- Create a new Script in MetaEditor (File, New, Script), paste the code and press F7 to compile.
- Open Market Watch in the terminal and confirm your pairs are listed. If your broker uses suffixes such as a trailing letter, type the exact names into the Symbols input.
- Drag the script from the Navigator onto any chart. The inputs window opens, because of the script_show_inputs property.
- Press OK and read the Experts tab. Each symbol prints one line.
Why ATR scales the margin
Average true range measures the typical size of a candle. A gap of five points between two averages means a great deal on a quiet pair and almost nothing on a volatile one. By requiring the gap to exceed a fraction of ATR, the same rule adapts to each market without you hand-tuning a separate number for every symbol. If you find the scanner calls everything FLAT, lower the margin to 0.05. If it calls almost everything a trend, raise it to 0.2 and compare the results with your own eye on the chart.
A note on timeframes and server time
Candle boundaries follow the broker’s server time, not your local clock. A 30-minute candle on your terminal closes on the broker’s half hour, which can differ from your computer’s clock by hours. This is normal, but it means two brokers can print slightly different candles for the same moment. When comparing scanner output between accounts, use the same broker.
What we could not confirm
- That a 10% ATR margin is the best value. It is a starting point you should test.
- Whether alignment of two timeframes improves results for your strategy. We did not backtest it.
Frequently asked questions
Why a script and not an indicator? A script runs once on demand and is easy to understand. An Expert Advisor with a timer is better for continuous scanning.
Will it slow my terminal? A handful of symbols is light. Very long lists create many handles, so keep the list short.
Key takeaways
Compare fast and slow averages on closed bars, scale the margin with volatility, report missing data honestly and treat disagreement between timeframes as a result, not an error.
Sources checked
- MQL5 Reference, iMA, iATR, CopyBuffer, SymbolSelect and StringSplit, mql5.com
Educational content only — not financial advice. Income and savings results vary and are never guaranteed. How we write
Enjoyed this story?
Get the best stories delivered weekly.


