> For the complete documentation index, see [llms.txt](https://help.hydravision.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.hydravision.ai/products/backtest-analysis/json-backtest-export.md).

# JSON Backtest Export

What the exported JSON backtest report contains and how to read it.

{% hint style="success" %}

## **It is not our intention for anyone to read the json file, we have included this for those who do. By default, import the json file into the portal Backtest repository for analysis.**

{% endhint %}

## Reading the JSON Report

At the end of a backtest Hydra Vision can write a complete machine-readable report as a `.json` file. This is the file the online report viewer reads, and the one to send if you want a run analysed.

It contains everything in the Journal summary plus a lot more — notably a **per-grid table** and a **daily equity curve** that the Journal cannot show.

***

### Where the file goes

```
...\MetaQuotes\Terminal\Common\Files\hydra_exports\{your_account_id}\
```

Filename pattern:

```
HV_Report_EURUSD_20251231_1.json
```

`{symbol}_{date}_{run}`. The **date is the last day of the test period** (the tester's clock when the report was written), not the day you ran it. The number at the end is a **run counter**: the first report for that symbol and date is `_1`, a re-run of the same test becomes `_2`, then `_3`, and so on. The EA takes the first number not already in the folder, so a re-run never overwrites an earlier report. Reports exported before 13 Oct 2026 end in a random three-digit number instead. Either way, take the exact filename from the Journal line below.

{% hint style="info" %}
The folder is under MetaTrader's **Common** files directory, shared by all terminals on the machine — not inside any one terminal's `MQL5` folder. In MetaTrader choose **File → Open Data Folder**, go **up one level** to the `Terminal` folder, then open `Common\Files\hydra_exports`.
{% endhint %}

### When the file is written

All of the following must be true:

* `Export Backtest Report` is enabled in the inputs.
* The test reached a proper shutdown — finished, or the EA was removed.

Confirmation appears in the Journal:

```
ExportBacktestReport: Backtest Standard report saved to HV_Report_...json
```

If you don't see that line, no file was written.

***

### Three things to know before you read it

#### 1. Missing is meaningful

Optional blocks are **left out entirely** rather than written as empty. An absent `grid_records` means *"no data was collected"*, which is different from *"zero grids happened"*. Check `meta.schema_capabilities` to see what this particular file contains rather than guessing from the version number.

#### 2. `null` means "not measured"

Where a value could not be measured, the report writes `null`, never `0`. `slippage_modelled: null` means MT5 does not expose it — not that slippage was zero.

#### 3. Times are offsets, money may be scaled

Two conventions in the big data blocks:

* **Times** are *seconds from `t0`*, where `t0` is broker-server midnight on the test's start date. This lets grid rows and the equity curve overlay each other without any date parsing.
* **Money** is in *minor units* — divide by the block's own `scale` field. With `scale: 100`, a value of `12450` means `124.50`.

{% hint style="warning" %}
All timestamps are **broker server time**, not UTC, unless your broker's server runs on UTC. This matters if you compare two reports from different brokers.
{% endhint %}

***

### The blocks, in order

#### `meta` — what was tested

Identity of the run: symbol, timeframe, period (as readable dates *and* as Unix seconds in `period_start_ts` / `period_end_ts`), test days, account currency, initial deposit, leverage, broker, terminal build, `generated_at` (your computer's clock when the file was written) and `backtest_type` (the data sample category you chose, such as `IN_SAMPLE`).

Key fields worth understanding:

| Field                                         | What it means                                                                                                                                                                                                                                                                                                                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `report_version`                              | The version of this file format. The current version is `3.3`. The online viewers read reports from `3.0` onwards and refuse anything older.                                                                                                                                                                                                                              |
| `ea_version` / `ea_build`                     | The EA version that ran the test, and the exact build of it.                                                                                                                                                                                                                                                                                                              |
| `calc_versions`                               | Which method was used for figures whose calculation has changed: `grid_costs`, `gridsense_side_net`, `hydra_dd` and `anchor_only_rate`. All are `2` on a current EA. A report without this block (older than `3.2`) used the methods from before 13 Oct 2026. The online viewers warn "Calculated differently" when you combine or compare reports whose versions differ. |
| `crit_cfg_hash`                               | Your **Strategy Print** — the fingerprint of the settings that decide what the strategy does. The file holds the 12 characters. If you publish the setfile's fingerprint, the same value is shown as `SP-XXXX-XXXX-XXXX`.                                                                                                                                                 |
| `cfg_hash`                                    | Your **Tuning Print** — the fingerprint of the tuning settings, shown as `TP-XXXX-XXXX-XXXX` if you publish it. Two runs with both prints the same used the same configuration — the fastest way to confirm you are comparing like with like. See Hydra Fingerprints.                                                                                                     |
| `hash_version`                                | Which version of the fingerprint calculation made the two prints (currently `hv4`). Prints are only comparable within the same version.                                                                                                                                                                                                                                   |
| `setfile_id`                                  | Older reports only (before `hv4`): the platform's ID for the setfile you tested. No longer written; backtests are matched to setfiles by their prints.                                                                                                                                                                                                                    |
| `modelling`                                   | The tick model you *declared*.                                                                                                                                                                                                                                                                                                                                            |
| `modelling_ticks` / `modelling_ticks_per_day` | The tick density actually *counted*.                                                                                                                                                                                                                                                                                                                                      |
| `schema_capabilities`                         | Array listing which optional blocks this file really contains.                                                                                                                                                                                                                                                                                                            |
| `open_at_close`                               | Positions and floating P\&L still open when the test ended.                                                                                                                                                                                                                                                                                                               |

{% hint style="info" %}
`modelling` is a label you set and could be wrong. `modelling_ticks_per_day` is measured and cannot be. If two runs differ by an order of magnitude in tick density, they are not comparable however they are labelled.
{% endhint %}

It also contains:

* **`instrument`** — contract size, point, digits, tick value/size, margin per lot, swap rates and swap mode. Everything needed to reinterpret the money figures on a different account.
* **`regime`** — what the market itself did during the period, independent of the EA. `null` if fewer than 30 bars were available.

**Reading `regime`**

<table data-search="false"><thead><tr><th>Field</th><th>What it means</th></tr></thead><tbody><tr><td><code>realised_vol_annual_pct</code></td><td>How volatile the period was, annualised.</td></tr><tr><td><code>atr_pct_of_price</code></td><td>Average bar range as a share of price.</td></tr><tr><td><code>net_move_points</code></td><td>Where price ended up versus where it started.</td></tr><tr><td><code>total_travel_points</code></td><td>Total distance price actually covered.</td></tr><tr><td><code>efficiency_ratio</code></td><td><code>net ÷ travel</code>. Near <code>1.0</code> = strong trend. Near <code>0</code> = choppy range.</td></tr><tr><td><code>max_run_no_retrace_points</code></td><td>The longest one-way run with no meaningful pullback.</td></tr><tr><td><code>retrace_threshold_points</code></td><td>Your grid's own step distance, used as the yardstick.</td></tr><tr><td><code>run_to_step_ratio</code></td><td>The two above, divided.</td></tr></tbody></table>

{% hint style="danger" %}
**`run_to_step_ratio` above `1.0`** means the period contained a one-way run longer than your grid's step — the exact condition that buries a grid. A profitable run with a high ratio survived a genuine stress event. A profitable run with a low ratio has simply not been tested yet.
{% endhint %}

***

#### `configuration` — the settings used

Every meaningful input: grid type, volume weighting and increments, step sizes, PCL strategy and which engines were enabled, SmartEntry settings, session flags, and the settings text the two prints were calculated from (`hash_string` for the Strategy Print, `noncrit_hash_string` for the Tuning Print).

Two blocks give detail the flags cannot:

* **`bias`** — `enabled`, `direction` (`"buy"`, `"sell"` or `null`) and `strength`. The older `bias_trade` boolean tells you a bias was set but not which way; this block tells you the direction.
* **`session_windows`** — `null` when the session filter is off. When on: `time_base` (`gmt` or `server`), the broker's GMT offset, and one entry per *enabled* session with `start_min` / `end_min` as minutes from midnight.

{% hint style="warning" %}
Two session details catch people out: `end_min` is **exclusive**, and if `start_min` is greater than `end_min` the window **wraps midnight** (the default GMT Asian session does exactly this). Also note `applies_to: "initial_grid_entry_only"` — the filter gates new grid entries only. **Open grids keep being managed outside the windows**, so you cannot infer "no activity outside these hours".
{% endhint %}

***

#### `profit` — the headline result

`total_net_profit`, `gross_profit`, `gross_loss`, `profit_factor`, `expected_payoff`, `total_costs`, and a `costs_breakdown`.

**Why the cost split matters**

| Field        | Behaves like                                                                                   |
| ------------ | ---------------------------------------------------------------------------------------------- |
| `commission` | Scales with **how many trades** you place.                                                     |
| `swap`       | Scales with **how long you hold** and in which direction.                                      |
| `fee`        | Reported but **excluded** from `total_costs` and from net — flagged by `fee_in_totals: false`. |
| `swap_mode`  | Whether your broker charges swap on this symbol at all.                                        |

Without the split you cannot tell "expensive to trade" from "expensive to hold". And if `swap_mode` is disabled, a `0.00` swap figure is a broker setting, not a measurement failure.

{% hint style="info" %}
`total_costs` and the `costs_breakdown` components come from different sources (per-grid accrual versus deal-level truth). They normally agree within a few cents; the per-grid figure can fall slightly short. The breakdown is taken straight from MT5's deals and is the one to trust.
{% endhint %}

***

#### `risk` — drawdown, from two sources

This block deliberately reports drawdown **twice**, and tells you which to trust.

| Sub-block      | Scope                                                                                                                                                       |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mt5_standard` | MT5's own figures. **Account-wide** — all symbols and all EAs. Use this to reconcile against the MT5 report.                                                |
| `hydra_dd`     | Hydra's own tracking. Scoped to **this EA on this symbol**: your starting balance plus this EA's closed trades (balance), plus its open positions (equity). |

`hydra_dd` states this itself in `basis`, `reconcile_against` and `authoritative_for_account`. Deposits, withdrawals and Safety Top-Up never move `hydra_dd` — it only ever contains this EA's own trading.

In both sub-blocks the same naming rule applies:

* `*_dd_monetary` / `*_dd_max_value` — worst drop in **money**.
* `*_dd_relative_percent` / `*_dd_max_percent` — worst drop in **percent**.
* `balance_*` = closed trades only; `equity_*` = includes floating positions.

`hydra_dd` adds `abs_dd_basis` — the capital figure the absolute-drawdown percentage was divided by. It is always your starting balance. It also carries `eq_abs_dd_value` / `eq_abs_dd_date` — how far this EA's equity fell below your starting balance, and when — and `last_capital_op`, the time of the last deposit or withdrawal on the account (empty if there was none).

**Why Hydra's drawdown differs slightly from MT5's**

On a single-symbol backtest **profit and commission match MT5 exactly**, to the cent. Drawdown does not, and it is not meant to — the two are measured in slightly different ways. Expect these small, explainable gaps:

| Figure                      | Typical gap                                                       | Why                                                                                                                                                                                                                                                                                                                      |
| --------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Balance drawdown**        | A few cents to a few dollars                                      | MT5 deducts a trade's entry commission the moment it opens; Hydra books the full commission when the trade closes. The totals are identical, but the low points land a few cents apart. This is also why MT5 can show a tiny Balance Absolute drawdown (e.g. `9,999.79` on a `10,000` deposit) where Hydra shows `0.00`. |
| **Equity drawdown**         | Hydra usually reads **deeper**, by up to about a percentage point | Hydra checks your equity on every price tick the EA processes, including the instant after it opens a position. MT5's report captures fewer of these moments, so it can miss the true low point. Hydra's figure is the more conservative of the two.                                                                     |
| **Multi-symbol / deposits** | Any size                                                          | `mt5_standard` is the whole account; `hydra_dd` is this EA only. On a multi-symbol account, or after a deposit or withdrawal, they measure different things.                                                                                                                                                             |

A worked example (GBPUSD, 279 days, 10,000 deposit):

|                  | MT5               | Hydra             |
| ---------------- | ----------------- | ----------------- |
| Net profit       | 2,072.57          | 2,072.57          |
| Balance drawdown | 362.44 (3.47%)    | 363.10 (3.47%)    |
| Equity drawdown  | 1,653.73 (16.36%) | 1,720.66 (17.02%) |
| Lowest equity    | 8,455.40          | 8,388.47          |

{% hint style="info" %}
**Rule of thumb:** profit should match exactly; balance drawdown within a few dollars; equity drawdown with Hydra the same or slightly deeper, typically by less than one percentage point. A gap well beyond that — or MT5 showing the *deeper* equity drawdown — is worth reporting.
{% endhint %}

**`breach_counters`**

Opt-in tiered counting of drawdown events. Five percentage tiers and five currency tiers, each with its `threshold` and `count`.

{% hint style="info" %}
It is a **waterfall**: each event is counted in its **deepest tier only**. An event that breached 3% appears in the 3% tier, not in the 1% and 2% tiers as well. Do not add the tiers together.
{% endhint %}

**`concurrency`**

Peak simultaneous exposure within this symbol: `peak_volume_lots`, `peak_open_dd`, `peak_margin_acct`, each with an `_at` time in seconds from `t0`, plus `both_sides_open_secs`.

`_at` is `null` when the peak never occurred — because `0` would be indistinguishable from "it happened at `t0`".

Finally: `recovery_factor`, `sharpe_ratio` and `margin_level`.

***

#### `efficiency`, `trades`, `grids`

| Block        | Contains                                                                                                                                 |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `efficiency` | `profit_per_day`, `profit_per_grid`, `profit_per_position`, `return_per_lot`. Use these to compare tests of different lengths.           |
| `trades`     | MT5's trade counts: total, long/short, winners/losers with percentages, largest win and loss.                                            |
| `grids`      | Grid counts (`total`, `buy`, `sell`), `total_positions`, `avg_positions_per_grid`, `max_depth`, `anchor_only_count`, `anchor_only_rate`. |

{% hint style="info" %}
In `grids`, `anchor_only_count` is the number of grids that never opened a second position, and `anchor_only_rate` is the same thing as a percentage of all grids (`anchor_only_count ÷ total × 100`). It is the same figure the GridSense panel shows as Anchor Only Rate. How those anchors were closed is a separate question — see the `anchor_sl` block.
{% endhint %}

{% hint style="warning" %}
`grids.max_depth` is a **position count** (the most positions any grid held). The Journal summary's **Max Grid Depth** is a **price distance**. Same word, different measurement.
{% endhint %}

***

#### Grid detail blocks

| Block              | Contains                                                                                                                                           |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grid_geometry`    | Max grid size, max volume, and peak position density high/low per side.                                                                            |
| `grid_timing`      | Total, average and longest grid duration per side, **in seconds**.                                                                                 |
| `grid_costs`       | Largest, average and total cost per side.                                                                                                          |
| `gridsense`        | Per-side and combined `grid_return`, `return_per_lot`, `net_profit`, plus `total_grid_drawdown` and `avg_grid_drawdown`, and a `volume` sub-block. |
| `grid_compression` | Grids analysed, average and max peak density per side.                                                                                             |

{% hint style="info" %}
`gridsense.global.total_grid_drawdown` is accumulated **per-grid** drawdown. It is a different measurement from account equity drawdown in `risk` — do not read them as the same number.

`gridsense.buy.net_profit` + `gridsense.sell.net_profit` adds up to `profit.total_net_profit` — a quick check that the per-side split is complete.
{% endhint %}

***

#### Attribution blocks — where the money came from

One block per profit mechanism — the nine ways a position can be closed for a result:

`pcl_engines` (with `config`, `dynamic`, `whole`), `pcl_inject`, `anchor_sl`, `position_hunter`, `multi_trailing`, `bounce`, `frontier`, `risk_max_loss`, `risk_step_gap`.

Each carries its own counts, its net result and a **`contribution_percent`**. Most use `closes`, `profit`, `loss` and `net`; `pcl_engines` reports `total_closes` and `total_profit`, and `pcl_inject` reports its injection counts and `net`.

`contribution_percent` is the field to compare. Together these nine blocks partition the total result, so **the percentages should sum to roughly 100**. The export checks this itself and raises a warning if the sum falls outside 95–105.

**`mechanisms`** — a top-level list (report `3.2` and later) that repeats the same nine in one place: each entry has an `id`, the `section` holding the full block, the `net_key` naming that block's net field, the `net` result and its `contribution_percent`. Every mechanism is listed, zeros included, so a missing entry would mean the EA has no such mechanism, not that it never fired.

Three more blocks sit beside them but are **counters, not mechanisms**. They carry no profit, loss or `contribution_percent`, and are not part of the 100%:

| Block              | Contains                                                                                                                                                      |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `safety_topup`     | Whether it was enabled, the buffer and trigger, deposits and withdrawals with their totals, peak and still deployed, consumed (unreturned), buffer remaining. |
| `new_pos_on_close` | `attempts`, `opened`, `success_rate`, and the three blocked counts (`blocked_too_close`, `blocked_dual_grid`, `blocked_count_low`).                           |
| `backfill`         | `evaluations`, `conditions_met`, `signal_blocked`, `fired`, `fire_rate`.                                                                                      |

{% hint style="warning" %}
`risk_max_loss` and `risk_step_gap` are protective hard stops, from your **Max Drawdown before auto close (negative)** and **Maximum missing steps gap** settings. By design they only ever realise losses. A negative contribution there is the feature working, not failing.
{% endhint %}

***

#### `smartentry` and `dormancy`

* **`smartentry`** — per side (`buy`, `sell`), split into `anchor` (opening a new grid) and `reentry` (adding to an open grid). Shows how often entries were allowed, which vetoes blocked them, and how often.
  * `anchor` carries `total_attempts`, `allowed`, `blocked` (with percentages) and one counter per anchor veto: `blocked_waterfall` (Anchor Waterfall Protection), `blocked_extended` (Block Late Entries), `blocked_extreme_dd` (Extreme DD Veto), `blocked_no_trend` (the MA Higher Time Frame market regime), `blocked_no_ma_cross` (Require MA Cross for Anchor), and from report `3.3` `blocked_micro_break` (Check Micro Break Structure on Anchor), `blocked_exhaustion` (Block Exhausted Moves) and `blocked_context` (the Market Context market regime with Buy Low/Sell High). `reached_final` counts the checks that had a live entry signal — the MA cross confirmed, or the cross requirement switched off.
  * `reentry` carries its own attempts, allowed and blocked counts, `blocked_extreme_dd`, per-zone counts and a `modifiers` sub-block. Only `reentry` has `modifiers`.
* **`smartentry.breakout`** (report `3.3`) — the breakout re-entry guard: `mode` (your Breakout Re-entries setting), `lag_days` (the Breakout Lag in use: `0` while Breakout Re-entries is Off, whatever the setting holds), `ease_days` (Breakout Ease), `up_breaks` and `down_breaks` (how often the market broke out of the lagged Market Context range), `frozen_m15_bars` (M15 bars in which an add was due but held back) and `capped_adds` (adds whose lot was capped). The counters stay `0` when Breakout Re-entries is Off or Breakout Lag is `0`.
* **`dormancy`** — how much of the test the EA was actually working: per-side grid cycles, active time, average grid duration and `utilization_percent`, plus `either_side_active_seconds`, `either_side_utilization_percent`, `dormant_seconds` and `dormant_percent`.

{% hint style="warning" %}
**SmartEntry counts are per checked tick, not per setup.** SmartEntry checks on every tick while it waits for an entry, and every check is counted. A blocked count is the number of ticks a veto said no — not the number of trades it stopped — which is why the figures can run into millions. One tick can also count under several vetoes at once, so the `blocked_*` counters do not add up to `blocked`.
{% endhint %}

{% hint style="info" %}
High dormancy is not a fault. It usually means the entry filters were selective. But a strategy that was only active 8% of the time has had far less real testing than the calendar length suggests.
{% endhint %}

***

#### `grid_records` — the per-grid table

**The most valuable block in the file.** One row per grid, for the entire test.

It is stored as a **column-oriented array** to keep the file small:

```json
"grid_records": {
  "scale": 100,
  "t0": "2025.01.01",
  "count": 53,
  "cols": ["id","gid","side","open","close","depth","vol",
           "peak_dd","peak_dd_at","mfe","mfe_at","net","costs","swap",
           "opens","closes","reason"],
  "rows": [
    [1,"1735722000",0,32400,46800,3,0.06,-1250,39600,880,44100,430,-12,-3,3,3,"frontier"]
  ]
}
```

Read each row positionally against `cols`:

<table data-search="false"><thead><tr><th>Column</th><th>Meaning</th></tr></thead><tbody><tr><td><code>id</code></td><td>Row number.</td></tr><tr><td><code>gid</code></td><td>Hydra's grid ID — also written into every order comment, so a row traces back to real deals.</td></tr><tr><td><code>side</code></td><td>Buy or sell.</td></tr><tr><td><code>open</code> / <code>close</code></td><td>Seconds from <code>t0</code>. <code>close</code> is <code>null</code> if still open at test end.</td></tr><tr><td><code>depth</code></td><td>Position count <strong>at the worst-drawdown instant</strong>.</td></tr><tr><td><code>vol</code></td><td>Lots open <strong>at that same instant</strong>.</td></tr><tr><td><code>peak_dd</code> / <code>peak_dd_at</code></td><td>Worst floating loss, and when.</td></tr><tr><td><code>mfe</code></td><td>Maximum Favourable Excursion — the best unrealised profit the grid ever showed.</td></tr><tr><td><code>mfe_at</code></td><td>When that peak occurred.</td></tr><tr><td><code>net</code></td><td><strong>Realised</strong> P&#x26;L. Floating P&#x26;L is excluded (see <code>meta.open_at_close</code>).</td></tr><tr><td><code>costs</code> / <code>swap</code></td><td>Costs attributed to this grid.</td></tr><tr><td><code>opens</code> / <code>closes</code></td><td>Whole-lifecycle fill and close counts.</td></tr><tr><td><code>reason</code></td><td>Why the grid closed — validate against the published <code>reason_enum</code>.</td></tr></tbody></table>

{% hint style="danger" %}
**`depth` is a snapshot, not a maximum.** It is the depth at the peak-drawdown moment, which makes it the right figure for a depth-versus-damage analysis, but it is *not* the grid's largest ever size. Peak depth for the whole test is `grids.max_depth`.
{% endhint %}

{% hint style="info" %}
Comparing `mfe` against `net` tells you how much profit a grid *showed* versus how much it *kept* — the clearest measure of whether your close logic is exiting too late.

An open row can still have a non-zero `net`, because a grid may have closed partials while remaining open. A row with no realised closes emits `0`, never `null`.
{% endhint %}

**When it is missing**

`grid_records` is omitted entirely on a live chart, during optimisation, or if the test opened no grids at all.

***

#### `equity_curve` — the account over time

Same conventions, same `t0`, so it overlays `grid_records` directly.

```json
"equity_curve": {
  "basis": "daily",
  "bucket_days": 1,
  "scale": 100,
  "t0": "2025.01.01",
  "points": 364,
  "cols": ["d","bal","eq_c","eq_lo","eq_hi","lo_at","hi_at"],
  "rows": [[0,1000000,1000000,999210,1000000,28800,null]]
}
```

| Column            | Meaning                                                              |
| ----------------- | -------------------------------------------------------------------- |
| `d`               | **Calendar** day offset from `t0`.                                   |
| `bal`             | Closing balance that day.                                            |
| `eq_c`            | Closing equity.                                                      |
| `eq_lo` / `eq_hi` | Lowest and highest equity that day.                                  |
| `lo_at` / `hi_at` | When those extremes occurred, in seconds from `t0`. `null` if unset. |

{% hint style="warning" %}
`d` is a **calendar** offset, not a row index. Gaps between rows are real non-trading days and must be preserved when plotting — otherwise weekends silently disappear and the curve is distorted.
{% endhint %}

Long tests are **bucketed** down to roughly 750 points to keep the file manageable. When that happens `basis` becomes `"bucketed"` and `bucket_days` tells you how many calendar days each row covers.

`eq_lo` is the block to check first: the lowest value across all rows is the worst equity point of the entire run. Expect it to be the same as `risk.mt5_standard.equity_min` or slightly **lower** — Hydra samples equity on every tick it processes and catches low points MT5's report can miss (see *Why Hydra's drawdown differs slightly from MT5's* above). When the two differ, the export notes it as warning `B1`.

***

#### `export_warnings` — read this if present

An array of strings, present **only when something did not reconcile.**

```json
"export_warnings": [
  "A9: grid_records row count 52 != STAT_TRADES 53",
  "V3: sum(contribution_percent) 93.2 outside [95,105]"
]
```

{% hint style="danger" %}
A fully clean report has **no `export_warnings` key at all**. If the key is present, treat every figure it mentions with suspicion until you understand why — with one common exception below.
{% endhint %}

{% hint style="info" %}
**`B1` (lowest equity differs from MT5) is usually expected.** When Hydra's `min(eq_lo)` is *lower* than `mt5_standard.equity_min` by a modest amount, Hydra simply caught a low point MT5's report did not. It is only worth investigating if Hydra's figure is *higher* than MT5's, or the gap is large.
{% endhint %}

The report is written **even when checks fail** — deliberately. Losing a multi-hour backtest over a one-penny rounding difference would be worse than shipping a flagged file. Warnings are also printed to the Journal as `[HV v2.8][WARN]` lines as they occur.

***

### A suggested reading order

1. `meta.crit_cfg_hash` and `meta.cfg_hash` (the Strategy Print and Tuning Print) — is this the run I think it is?
2. `export_warnings` — is anything untrustworthy? (A modest `B1` is normal.)
3. `risk.mt5_standard.equity_dd_monetary` — the real worst case.
4. `profit.total_net_profit` and `risk.recovery_factor` — paid enough for it?
5. `meta.regime.run_to_step_ratio` — was this period actually a test?
6. `contribution_percent` across the attribution blocks — what earned the money?
7. `grid_records` — sort by `peak_dd` and look at the worst handful of grids.

{% hint style="warning" %}
A single backtest, however detailed the report, is not evidence of future profitability. Vary the period, the symbol and the modelling quality, and check `modelling_ticks_per_day` before comparing any two reports.
{% endhint %}
