The Numbers Are Not in the Code

Published: 2026/09/16 UTC Updated: 2026/09/16 UTC Permalink

You now have a strategy that decides and an engine that acts. Neither of them is worth anything without a third thing: the numbers.

An EA is not "the Trend strategy". It is the Trend strategy with an EMA pair of 70 and 190, an RSI floor of 48, an ATR window starting at 35 points, a stop of 1.6 ATR and a target at 2.2R, on EURUSD M15, between 00:00 and 22:00 at UTC+9. Change any one of those and you have a different EA with a different result. Most people discover this the moment they try to reproduce a backtest from three weeks ago and cannot.

So the values get their own file, their own version, and — this is the part almost nobody does — their own reasons.

Inputs are a schema, not a preamble

Every strategy has an inputs header of its own: Inputs/Trend_Inputs.mqh, TokyoFix_Inputs.mqh, and so on. Nothing else lives there.

//| Purpose                                                          |
//| Inputs-only file for the Kurosawa EA suite (Excel-aligned).      |
//|                                                                  |
//| Units & conventions                                              |
//| - ALL distance-based values are in POINTS                        |
//| - POINTS are in _Point units for the current symbol              |
//|                                                                  |
//| Notes                                                            |
//| - Keep strategy-relevant inputs clean and explicit               |
//| - Unused schema placeholders are grouped at the bottom           |
//| - Tune per-symbol and per-session. Do not assume universality    |

Two things there are worth stealing outright.

One unit, stated once. Every distance is in points. Not pips, not price, not "whatever that indicator returns". Mixed units are the most common source of a stop that is off by a factor of ten, and the fix is a sentence at the top of a file rather than vigilance forever.

"Do not assume universality." These values are tuned for one symbol in one session. A header that says so stops the next person — usually you, later — from copying the file to GBPJPY and wondering why it behaves badly.

Three versions, and they are not the same thing

This looks redundant and is not:

// 0.7.1 = engine clock -> broker server time (2026-09-09)
// Compile-time build. InpEaVersion is an INPUT and a .set can override it; this constant is what actually
// runs, and the Init line prints both so a mismatch is visible in the log (AB t-67a9014).
#define TREND_BUILD "0.7.2"
input string InpEaVersion    = "0.7.1";
// The TUNE version - one set of parameter values. Bumped on ANY parameter
// change. Distinct from InpEaVersion above, which is the ENGINE build.
// Presets and backtests on 1kpips.com key on this field, not on InpEaVersion.
input string InpPresetVersion = "0.1.0";

Read the first comment again, because it describes a genuine hazard. InpEaVersion is an input, which means a settings file can set it to anything. A backtest report can therefore claim it ran engine 0.4.0 while the compiled binary was 0.7.2. The report would not be lying on purpose; it would simply be repeating a number someone typed into a file months ago.

TREND_BUILD cannot be overridden by anything — it is baked in at compile time. Printing both at startup means a mismatch shows up in the log instead of in a conclusion.

And the third one does the real work. InpPresetVersion is the tune: one specific set of values, bumped whenever any of them changes. It is what the preset pages and backtest records on this site are keyed on — because "which numbers produced this result" is a more useful question than "which engine build was it".

If you take one habit from this module, take that separation. The code has a version. The settings have a different version. A result belongs to the second one.

Every number that is not obvious carries its reason

Here is a single input, with its comment intact:

// Lowered from February's 60 on 2026-09-03. The first live daily summary
// showed Bars=3 / atr=3 - the 60-point floor (6 pips of ATR(14)) rejected
// every bar on EURUSD M15, so the ADX, bias and signal gates downstream of
// it were never even reached and could not be observed.
//
// NOTE this is a DIAGNOSTIC setting, not a validated one. At the 12-point
// spread cap and InpSlAtrMult 1.6, an ATR of 35 puts spread at ~21% of the
// stop - acceptable at 0.01 lots to gather gate data, NOT acceptable when
// sizing up. Resolve the floor and InpSlAtrMult together from the Feb-Sep
// backtest before increasing risk.
input double   InpAtrMinPoints   = 35.0;

That comment is longer than most people's entire strategy file, and it is doing four jobs at once.

It records what changed and when. It records the evidenceBars=3 / atr=3, meaning the EA evaluated three bars and the ATR gate rejected three of them. Those are the diagnostic counters from module 2, doing exactly what they were built for: the gate that was eating everything named itself, and nothing downstream could even be observed until it was fixed.

It records the current value's status — diagnostic, not validated. That is a genuinely rare and valuable admission to leave in a file. A number that is there to gather data is not the same as a number that earned its place, and a future reader has no way to tell them apart unless you say so.

And it records what must happen before the risk goes up. Resolve the floor and the stop multiple together, from a specific backtest window, before sizing up.

Parameters that only make sense in relation to each other

// Tightened from the February .set value of 20. At InpSlAtrMult 1.6 on
// EURUSD M15 a 20-point cap is roughly 31% of the stop distance; keep this
// at or under ~15% of (InpSlAtrMult x typical ATR points). If block_spread
// in the daily summary runs high, widen the stop rather than loosening this.
input int      InpMaxSpreadPoints             = 12;

A spread cap in isolation is meaningless. Twelve points is tight on EURUSD M15 and absurd on GBPJPY H1. What makes it meaningful is the ratio: spread should stay under about 15% of your stop distance. Below that, costs are a rounding error on the trade. Above it, you are paying a third of your risk to the broker before the idea gets a chance.

Note the last sentence, which is a decision rule rather than a value: if the spread gate starts rejecting a lot of bars, widen the stop — do not loosen the cap. Loosening the cap lets expensive trades through and flatters your trade count. Widening the stop changes the geometry honestly and shows up in the results.

The setting that can deadlock your EA

input int      InpMaxConsecLosses             = 2;
// Must stay true: consecLosses only resets on a win, so with this false the EA
// halts permanently after N losses - it cannot win because it cannot trade.
input bool     InpResetConsecLossDaily        = true;

Two losses in a row and the EA stops trading. The counter resets on a win — but you cannot win if you are not trading. Without the daily reset, the second loss is terminal: the EA sits on the chart forever, looking like it is working, having quietly retired.

Worth generalising. Any safety limit whose release condition requires the thing it just blocked is a deadlock. Check every guard you write for that shape.

Magic numbers are identity, not decoration

// Magic must be unique per RUNNING INSTANCE - see the registry in
// Helpers/KurosawaHelpers.mqh. Two EAs sharing a magic on one symbol will
// enumerate and close each other's positions.
input int    InpMagic        = 2026090301;

The magic number is how an EA recognises its own positions. Two instances sharing one on the same symbol do not simply coexist — each sees the other's trade as its own, manages it, trails it, and closes it. Both EAs behave "correctly" and the account does something neither of them intended.

Hence a registry rather than a habit of typing today's date.

The .set file, and the trap in it

Values reach a compiled EA through a .set file. Here is a real one, saved by the tester:

; saved automatically on 2026.09.09 13:41:51
; this file contains last used input parameters for testing/optimizing TrendEA expert advisor
;
InpZone=NewYork
InpEaName=Trend_Multi_M15_NY
InpMagic=2026090912||2026090912||1||20260909120||N
InpEaId=ea-trend-multi-m15-ny
InpEaVersion=0.7.1
InpPresetVersion=0.1.0
InpTargetPair=
InpTargetTf=15||0||0||49153||N
InpEmaFast=70||70||1||700||N
InpAtrMinPoints=20.0||20.0||2.000000||200.000000||N

Three things to notice, all of which bite when you generate these files programmatically — and you will, as soon as you are running more than a handful of tunes.

Numeric inputs carry four extra fields: value||start||step||stop||optimize. That is the optimiser's range. InpEmaFast=70||70||1||700||N means the value is 70, and if you switch optimisation on it would sweep 70 to 700 in steps of 1.

String inputs carry none of it. InpZone=NewYork, and that is the whole line. If you append || fields to a string input the way you do for a number, MT5 does not parse them — it takes the entire rest of the line as the string. Your EA id becomes ea-trend-multi-m15-ny||ea-trend-multi-m15-ny||1||... and every record it writes is junk. This is not hypothetical; it is how a batch of generated presets loaded as garbage here on 8 September 2026.

Enums are numbers. InpTargetTf=15 is PERIOD_M15, not the string. And the files are UTF-16, so a generator that writes UTF-8 produces a file the tester silently ignores.

One more thing in that file worth sitting with: InpAtrMinPoints=20.0, while the source default two sections up is 35.0. The defaults in your source code are not what ran. They are a starting point that a settings file overrode. Any time you reason about a past result from the code alone, you are guessing.

What this buys you

A result you can hand to someone else.

Every preset on this site publishes its tune version, its parameter values and the backtests that were run against them — including the New York probe the settings file above belongs to, which lost on all three pairs it touched: profit factors of 0.72, 0.67 and 0.76 across 692 trades. The numbers are there because a result without its settings is an anecdote, and an anecdote is not evidence of anything.

You can check the whole catalogue at the presets page, or pull the same rows as JSON from /api/presets if you would rather diff them yourself.

What you should have now

Inputs in their own file with a stated unit, a tune version separate from the engine version, a reason attached to every value that has one, and a healthy suspicion of any backtest that arrives without the settings that produced it.

Next module: risk sizing — the layer that decides how much, and why it is the only part of an EA where being conservative has no downside.

If this helped your EA work, share it.
X Facebook LinkedIn

Keisuke Kurosawa
Hello

Comments

0
No comments yet.

To add a comment, please log in.
Share
https://1kpips.com/en/blog/inputs-and-preset-files
Categories
Learn
Tags
MQL5, MT5, Expert Advisor, EA development, preset files, backtesting, reproducibility, tutorial, course

Related Articles

Next step
Save this idea into your EA: add a session filter, then backtest with and without it to see the difference.