Your EA made money on EURUSD for six months. You attach it to gold — same settings, same logic, same expectations. Within a week, it has either lost money or opened zero trades. No error message, no crash, just wrong results. The problem is not the market. It is five symbol-specific assumptions baked into the code that only work on the one instrument you tested.
Most EAs are developed and tested exclusively on EURUSD. The five hardcoded assumptions they carry — digit count, lot constraints, tick value, spread thresholds, and session timing — guarantee silent failure the moment they run on a different instrument. Across 20+ cross-symbol debugging projects over the past three years, I have seen the same five assumptions cause the same failures. A trader scales an EA to new symbols and blames the market when it loses. The market is not the problem. The code is.
The Scaling Instinct
A trader runs a grid EA on EURUSD for six months. Consistent results, reasonable drawdown. The logical next step: attach it to XAUUSD, GBPJPY, maybe an index. More symbols, more opportunity.
The EA loads. No error. The chart looks normal. Trades start appearing.
Then the losses start — or worse, nothing happens at all. Zero trades for days. No error message in the Experts log. The EA is running, the chart is active, AutoTrading is enabled. It simply does nothing.
In this case, the grid EA’s lot sizes were wrong on gold (0.01 on gold means something completely different than on forex), its pip calculations were off, and its spread filter — tuned for EURUSD’s typical 1.5-pip range — blocked every trade on gold where 30+ point spreads are normal. Five lines of code, five wrong assumptions, and a profitable system became a silent failure.
The trader blamed gold’s volatility. The fix was in the code.
Assumption 1: Digit Count and Pip Calculation
The most common hardcoded assumption in EAs I review: using `Point * 10` to convert points to pips. On 5-digit forex pairs like EURUSD and GBPUSD, this works. On everything else, it breaks.
Consider a trailing stop set to 20 pips. The code calculates `trailDistance = 20 * Point * 10`.
- EURUSD (5 digits): Trails at 20 pips. Correct.
- USDJPY (3 digits): Trails at 200 pips. The trailing stop never activates because price never moves that far.
- US30 (2 digits): Trails at 2,000 points. Meaningless.

The EA does not throw an error. It calculates a number that looks valid and uses it. The stop loss is technically set — just at a distance that makes the logic useless.
The fix: query `SymbolInfoInteger(Symbol(), SYMBOL_DIGITS)` at startup and calculate the pip multiplier based on the actual instrument, not a hardcoded assumption. A 5-digit pair needs `Point * 10`. A 3-digit pair needs `Point * 10` as well — but the scale changes. A 2-digit index needs a completely different calculation. One function call replaces the assumption.
Assumption 2: Lot Size and Step
Hardcoding 0.01 as the minimum lot size is the second most common failure. On standard forex accounts, 0.01 works. On gold, many brokers require 0.10 minimum. On indices, the minimum and lot step vary by broker and instrument — 1.0, 0.1, 0.01, there is no universal default.
At barmenteros FX, we see this in roughly half of the cross-symbol EA rescue projects we take on: the EA’s lot logic was built for forex and never validated against the target instrument’s constraints. I reviewed one client EA that calculated the lot size, then clamped it with `MathMax(calculatedLot, 0.01)` and rounded to two decimal places. On EURUSD, every order filled. On XAUUSD with that broker, the minimum lot was 0.10 with a step of 0.10. Every single `OrderSend` call was rejected because 0.01 is not a valid lot for gold on that broker.
The EA opened zero trades on gold. No error message appeared because the EA had no error handling on its order calls — a separate problem, but one that makes this assumption invisible.
The fix: always query the symbol’s lot constraints at runtime. `MODE_MINLOT`, `MODE_MAXLOT`, and `MODE_LOTSTEP` in MT4. `SYMBOL_VOLUME_MIN`, `SYMBOL_VOLUME_MAX`, and `SYMBOL_VOLUME_STEP` in MT5. Round the calculated lot to the nearest valid step. If the result is below the minimum, either use the minimum or skip the trade — but do it deliberately, not by accident.
Assumption 3: Tick Value and Profit Calculation
On EURUSD with a USD-denominated account, the math is clean: 1 pip on a 0.01 lot is roughly $0.10. Traders internalize this ratio. Developers hardcode it. On any other symbol, that ratio is wrong.
I debugged a position-sizing module that calculated risk per pip as `0.10 * lotSize / 0.01` — correct only for EURUSD on a USD account. The trader attached it to GBPJPY. With GBPJPY trading around 190.00 at the time, the actual tick value for that cross was roughly 40% higher than EURUSD’s due to the GBP-to-USD conversion embedded in the JPY cross rate.
The mismatch between assumed risk and actual risk:
| EURUSD (assumed) | GBPJPY (actual) | |
|---|---|---|
| Intended risk per trade | 2.0% | 2.0% |
| Actual risk per trade | 2.0% | ~2.8% |
| Over 5-trade drawdown | 10.0% | ~14.0% |
The trader believed they were risking 2% per trade. The actual exposure was 2.8%. On a single trade, the difference is tolerable. Over a 5-trade drawdown sequence — which is normal for any strategy — the cumulative error meant hitting the trader’s psychological pain point two trades earlier than expected. They pulled the EA manually, blaming the strategy. The strategy was fine. The risk calculation was wrong because it assumed EURUSD math on a JPY cross.
The platform provides the correct value in real time. `MarketInfo(Symbol(), MODE_TICKVALUE)` in MT4 or `SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE)` in MT5 returns the actual monetary value of one tick movement for one lot, updated with every price change. One function call replaces the hardcoded assumption.
Assumptions 4 and 5: Spread Behavior and Session Hours
These two are related — both involve market microstructure that differs dramatically between instruments.
Spread. EURUSD averages 1–2 pips during London and New York sessions. A spread filter set to reject trades above 3 pips catches only extreme conditions — news events, low liquidity gaps. On XAUUSD, spreads of 30+ points are normal. On indices, spreads spike during the open and close.
I reviewed an EA with `if(spread > 30) return;` as its spread filter. On EURUSD with 5-digit pricing, a spread of 30 points equals 3 pips. The filter worked perfectly — it blocked trades during news.
On XAUUSD, the spread was never below 30 points during that broker’s normal conditions. The EA filtered 100% of trades. It ran for two weeks without opening a single position. The trader thought the strategy had no signals on gold. The strategy had plenty of signals. The spread filter rejected all of them.
Session hours. Forex trades nearly 24 hours. Metals and indices do not. They have session breaks — sometimes hours long — with zero incoming ticks. An EA designed for continuous data does not know how to handle a gap.
A session-based EA designed for “Asian session trading” assumed continuous tick data. Attached to DAX, it received zero ticks during Asian hours because the European market was closed. When the session opened with a gap, the EA evaluated its entry condition against the last tick from the previous day — stale data — and entered immediately on the opening bar with an entry price that did not reflect current conditions.
The fix for both: treat spread thresholds and session windows as per-symbol inputs, not universal constants. Either parameterize them so the trader adjusts per instrument, or query the symbol’s session schedule with `SymbolInfoSessionTrade()` and set spread baselines dynamically from recent history.
The Fix Is Architectural, Not Cosmetic
Making an EA symbol-agnostic is not about patching five lines of code in isolation. It is an architectural decision — one that should happen at design time, not as a retrofit after the EA fails on a new symbol.
Every production EA should execute five runtime queries on initialization:
- Digits and point size — Calculate the pip multiplier from `SYMBOL_DIGITS`, not from a hardcoded `Point * 10`.
- Lot constraints — Read minimum lot, maximum lot, and lot step. Round every lot calculation to the nearest valid step.
- Tick value — Use `SYMBOL_TRADE_TICK_VALUE` for all risk and profit calculations. Never hardcode pip values.
- Spread baseline — Read the current spread at startup, log it, and validate that the EA’s spread filter is realistic for this symbol.
- Session schedule — Query the trading session hours. If the EA expects continuous ticks and the symbol has session breaks, log a warning.
If any value falls outside what the EA’s logic can handle, it should log a clear message and stop — not trade with wrong assumptions. At barmenteros FX, building symbol-agnostic EAs from the start is standard practice in our AI-assisted development process — across every custom EA project we deliver. When we receive single-symbol EAs for rescue, the retrofit typically costs more than the original development.
Retrofitting is possible. But every one of these five queries costs a fraction of a second to implement at design time and hours of debugging when it is missing in production.
Before attaching your EA to a new symbol, check these five against what the code assumes. If any of the five do not match, the EA is not ready for that instrument. This is the difference between an EA that trades one pair and an EA that actually trades.
If your EA was built for one pair and you need it working across instruments, request a free assessment. Cross-symbol compatibility is one of the most common fixes in our EA rescue projects.



Leave a Reply