Five Symbol-Specific Assumptions That Silently Break EAs on Non-EURUSD Instruments
Most EAs I debug for cross-symbol failures share the same five hardcoded assumptions. They are all correct on EURUSD and all wrong on everything else. Here is what breaks, why, and the runtime queries that fix each one.
Digit Count and the Point * 10 Trap
The most common hardcoded assumption: using Point * 10 to convert points to pips. On 5-digit pairs like EURUSD, this works correctly. On a 3-digit pair like USDJPY, the multiplier produces values an order of magnitude too large. A 20-pip trailing stop becomes 200 pips. On a 2-digit index, it is completely meaningless.
The fix is straightforward — query the actual digit count at startup:
Call this once in OnInit() and store the result. Every pip-based calculation in the EA uses the stored value instead of a hardcoded multiplier.
Lot Constraints Vary by Symbol and Broker
Hardcoding 0.01 as the minimum lot size works on most forex accounts. On gold, many brokers set the minimum at 0.10 with a step of 0.10 . On indices the constraints vary further — 1.0 , 0.1 , or 0.01 depending on broker and instrument.
When an EA sends an order with an invalid lot size, the result depends on error handling. If the EA checks the return value of OrderSend , it gets error 131 (invalid volume). If it does not, the order silently fails and the EA opens zero trades with no explanation in the journal.
This normalizes any calculated lot to the nearest valid step for the target symbol. The divide-by-zero guard on lotStep prevents a crash if symbol data is unavailable.
Tick Value Is Not Universal
On EURUSD with a USD-denominated account, 1 pip on a 0.01 lot is roughly $0.10. Developers hardcode this ratio. On a cross like GBPJPY, the actual tick value is significantly different because it includes the GBP-to-USD conversion embedded in the JPY cross rate.
The platform provides the correct value at runtime:
One function call replaces the hardcoded assumption. The risk-per-pip calculation is now correct regardless of the account currency or symbol.
Spread Filters Need Per-Symbol Calibration
A spread filter set to reject trades above 30 points catches extreme conditions on EURUSD where the typical spread is 10-15 points (1-1.5 pips). On gold, 30+ point spreads are normal market conditions. The EA filters 100% of trades and runs for weeks without opening a position.
The fix: treat the spread threshold as a per-symbol input, or derive it dynamically from recent spread history at startup:
Log the spread at startup so you can see whether the filter is realistic for the attached symbol. If the current spread already exceeds the filter on a quiet market day, the filter is misconfigured — not the market.
Session Awareness Prevents Stale Data Entries
Forex trades nearly 24 hours. Metals and indices have session breaks lasting hours. An EA designed for continuous tick data does not know how to handle a gap. When the session opens, the EA evaluates conditions against the last tick from the previous day and may enter immediately on stale data.
SymbolInfoSessionTrade() returns the trading session schedule. If the EA expects continuous data and the symbol has session breaks, log a warning at initialization:
If the session window does not cover the hours the EA expects to trade, the EA should alert the user rather than trade on stale prices.
The Five-Query Checklist
Every production EA should run these five queries in OnInit() :
- SYMBOL_DIGITS — Calculate pip multiplier from actual digits
- SYMBOL_VOLUME_MIN/MAX/STEP — Normalize every lot calculation
- SYMBOL_TRADE_TICK_VALUE — Use platform-provided tick value for risk math
- SYMBOL_SPREAD — Log current spread and validate filter realism
- SymbolInfoSessionTrade() — Check session hours against EA expectations
If any value falls outside what the EA's logic can handle, log a clear message and stop. Trading with wrong assumptions is worse than not trading.


