--json output reference¶
This page is the contract (schema) reference for the alpha-forge CLI's --json output. It defines the top-level fields, types, and envelope conventions of the main commands, plus the meaning and increment rules of schema_version / forge_version, so agents and MCP/pipe consumers have a stable reference point when parsing output.
The conventions themselves (stdout purity, envelopes, exit codes) live in CLI conventions for agents. This page complements that with "the actual fields that appear".
Runtime catalog
The machine-readable catalog of every command (which commands have --json, the type of each option) is available via alpha-forge system describe --json. This page covers "what fields a --json output returns"; system describe --json covers "which commands have --json".
Three envelope shapes¶
Output shapes fall into three families by command type.
- List commands (
list/scan-all, etc.): a{"<plural>": [...], "count": n}envelope. Even when empty, an empty array + exit code0. - Single-run commands (
backtest run/optimize walk-forward/backtest monte-carlo, etc.): a raw object with no envelope (metrics and meta fields sit at the top level). - Errors: a fixed 3-key
{"error": ..., "code": ..., "id": ...}(see Error output).
Percent fields (*_pct) are already in percent units (e.g. 17.39 means +17.39%). Do not multiply by 100 again.
schema_version / forge_version¶
The backtest run --json output includes these two meta fields.
| Field | Type | Meaning |
|---|---|---|
schema_version |
int | The schema generation of the CLI output JSON. Currently 1. Bump it per the rules below |
forge_version |
string | The alpha-forge version that produced this output (e.g. "0.15.0") |
Distinct from the strategy JSON's schema_version
This schema_version is the contract generation of the CLI output JSON, a different concept from the strategy JSON file's schema_version (the strategy-definition schema generation seen in strategy show --json).
Increment rules (when to bump)¶
Bump schema_version by +1 only on a backward-incompatible change.
| Change | Bump? |
|---|---|
| Removing / renaming an existing field | Yes |
| Type change / meaning change of an existing field | Yes |
| Adding a new field (existing fields unchanged) | No (additions are backward-compatible) |
| Changing the envelope shape | Yes |
| Formatting only (compact ↔ indent, key order) | No |
The rule of thumb: additions are compatible; removals/renames/type or meaning changes are not. forge_version advances naturally every release, so use forge_version to track "when something changed" and schema_version to decide "whether consumer code must be fixed".
backtest run --json¶
A raw object with no envelope; metrics sit at the top level (60+ fields in practice). Only the contract-relevant meta fields are listed here.
| Field | Type | When present | Meaning |
|---|---|---|---|
schema_version |
int | always | CLI output schema generation |
forge_version |
string | always | producing version |
warnings |
array | always | runtime cautions (e.g. low trade count) |
freemium_limit_notices |
array | always | Trial-plan limit notices (include a code) |
run_id |
string | when a real run exists | run identifier |
result_id |
string | when a result is saved | saved-result identifier |
pre_filter_pass |
bool | on pre-filter evaluation | whether the pre-filter passed |
pre_filter |
object | on pre-filter evaluation | applied thresholds (sharpe_min / max_dd_max / monthly_volume_usd_min / min_trades / goal) |
criteria_check |
object | when pass/fail criteria exist | verdict (merged into the body) |
next_step |
array | when next actions are suggested | next-command candidates (string array) |
carry_adjusted_metrics |
object | when --carry accrued carry |
reference metrics including FX carry (rate-differential approximation). Key presence is the contract (absent when not specified or carry could not be accrued). IS-period value when combined with --split |
carry_adjusted_note |
string | when carry_adjusted_metrics exists |
note on the approximation method and its limits |
With --split, IS/OOS comparison meta is added: out_of_sample_metrics (OOS metrics), overfitting_score, overfitting_risk, and walk_forward_summary (is_sharpe / oos_sharpe / is_return_pct / oos_return_pct / overfitting_score / overfitting_risk).
With --summary, heavy per-trade / per-bar arrays are dropped for a lightweight JSON (the meta-field contract is unchanged).
optimize walk-forward --json¶
A raw object with no envelope.
| Field | Type | Meaning |
|---|---|---|
symbol / strategy / metric |
string | symbol / strategy ID / optimization metric |
windows |
array | per-window results (IS/OOS metrics, skip_reason, etc.) |
is_valid_windows / valid_oos_windows / total_windows / skipped_windows |
int | window counts |
all_is_invalid |
bool | whether IS optimization failed in every window |
freemium_limit_notices |
array | Trial limit notices |
opt_run_id |
string | null | run_id of the optimization_runs row recorded with --save (null without --save or on recording failure; the key is always present) |
pre_filter_pass |
bool | null | whether the average OOS metric meets the threshold (null if undecidable) |
pre_filter |
object | applied thresholds (sharpe_min / max_dd_max / min_trades / goal) |
pre_filter_pass / pre_filter are always present even without --goal (using default thresholds), matching backtest run's naming and contract.
backtest monte-carlo --json¶
Runs a Monte Carlo simulation from a saved backtest result's trade history. A raw object with no envelope.
| Field | Type | Meaning |
|---|---|---|
initial_capital |
number | initial capital |
simulations_run |
int | number of runs |
mean_final_equity / median_final_equity / worst_final_equity / best_final_equity |
number | final-equity statistics |
mean_max_drawdown_pct / max_drawdown_95pct |
number | max drawdown (mean and 95th percentile) |
ruin_probability_pct |
number | probability of ruin (%) |
pre_filter_pass |
bool | null | whether ruin probability is at or below the default threshold (5%) |
pre_filter |
object | max_ruin_pct (default 5.0) |
If the input result has fewer than 10 valid trades, an error is printed to stderr and exit code 1 is returned (no pure JSON is emitted).
backtest dca --json¶
Dollar-cost averaging simulation. A raw object without an envelope. Numbers are not rounded (only irr_pct is rounded to 4 decimals). See backtest dca in the CLI reference for the command itself.
Single window (without --rolling-years)
| Field | Type | Meaning |
|---|---|---|
symbol / start / end |
string | Symbol and window (dates are YYYY-MM-DD) |
total / months / buy_day / cost_pct |
number / int | null / string / number | Total amount, number of buys (null when omitted = window months), buy day, one-way cost (%) |
final / invested / cash / units |
number | Final value, amount invested, uninvested cash (normally 0), units held |
buys / boosted |
int | Number of buys, number of buys whose boost condition was true (zero-amount buys are not counted) |
underwater |
bool | final < invested (unrounded) |
mdd_pct / irr_pct |
number | Max drawdown of the value (%), money-weighted annual return (%) |
curve |
array | Daily {"date", "value", "invested"} |
lump |
object | null | With --compare-lump: the lump sum (buys the whole total on the window's first business day). Same keys without curve |
All windows (--rolling-years N)
| Field | Type | Meaning |
|---|---|---|
years / range |
int / array | Window length in years, and the range the windows are taken from [start, end] |
rows |
array | Per window: start / end / final / invested / underwater / irr_pct / mdd_pct / boosted / lump_final / lump_wins (the last two have values only with --compare-lump, otherwise null; lump_wins is lump_final > final, unrounded) |
summary |
object | n_windows / n_underwater / n_lump_wins (null without --compare-lump) |
Validation errors (both --boost-dd and --boost-sma, an invalid --buy-day, --months beyond the window, etc.) exit with code 2; an unknown --cost-preset or a preset with fixed_per_share exits with code 1.
backtest withdraw --json¶
Withdrawal simulation (fixed/percent, inflation-adjusted, guard rules). A raw object without an envelope. Numbers are not rounded. See backtest withdraw in the CLI reference for the command itself.
Single window (without --rolling-years)
| Field | Type | Meaning |
|---|---|---|
symbol / start / end |
string | Symbol and window (dates are YYYY-MM-DD) |
initial / rate_pct / mode / cpi |
number / number / string / string | null | Principal, withdrawal rate (%), fixed/percent, --cpi storage key (null when omitted) |
guard / guard_cut_pct / cost_pct |
string | null / number / number | Guard rule (yoy/dd:N/null), cut % when it triggers, one-way cost (%) |
final / final_real |
number | Final value, nominal and real (equal to each other without --cpi) |
depleted / depleted_year |
bool / int | null | Whether the window ran out mid-way, and which withdrawal (null if never depleted) |
withdrawn_total / withdrawn_real_total |
number | Total withdrawn, nominal and real |
min_withdrawal_ratio |
number | Smallest real withdrawal ÷ the first real withdrawal |
guarded |
int | Number of times the guard triggered |
schedule |
array | Per-withdrawal {"date", "amount", "amount_real", "balance_after"} |
All windows (--rolling-years N)
| Field | Type | Meaning |
|---|---|---|
years / range |
int / array | Window length in years, and the range the windows are taken from [start, end] |
rows |
array | Per window: start / end / final / final_real / depleted / depleted_year / withdrawn_total / withdrawn_real_total / min_withdrawal_ratio / guarded (no schedule) |
summary |
object | n_windows / n_depleted |
Validation errors (--guard with --mode percent, an invalid --guard format, --guard-cut not 0/10/20, --rate out of range, --initial <= 0, CPI not covering a window's withdrawal date, etc.) exit with code 2; a --cpi series that has not been fetched exits with code 1.
data fx-convert --json¶
Converts a stored index into another currency and saves it under a new name. A raw object without an envelope. The warning for FX forward-filled on more than 2% of rows goes to stderr, so stdout stays pure JSON. See data fx-convert in the CLI reference for the command itself.
| Field | Type | Meaning |
|---|---|---|
symbol / save_as |
string | Source symbol and the saved name |
interval |
string | Always "1d" |
fx |
string | FRED key of the FX rate |
hedge |
bool | Whether --hedge was set |
rate_quote / rate_base |
string | null | FRED keys of the rates (null without --hedge) |
start / end |
string | First and last day of the saved series (YYYY-MM-DD) |
rows |
int | Number of saved rows |
filled_days |
int | Days on which FX was forward-filled |
path |
string | Where the series was saved |
Validation errors (invalid --save-as, wrong --hedge / rate-key combination, an empty --start / --end slice, FX or rates not covering the period, etc.) exit with code 2; a missing source daily series or FRED series exits with code 1.
List envelope¶
List / scan commands return {"<plural>": [...], "count": n}. Even when empty, an empty array + exit code 0.
| Command | Envelope keys | Main element keys |
|---|---|---|
strategy list --json |
strategies / count |
strategy_id / name / version / asset_type / timeframe / tags / notes / created_at / updated_at |
backtest list --json |
results / count |
saved-result rows (strategy_id / symbol / key metrics) |
analyze indicator list --json |
indicators / count |
name / category (plus params with --detail) |
analyze pairs scan-all --json |
pairs / count |
cointegration test results (plus cointegrated_count) |
system describe --json |
commands / count |
command / path / options / json_supported |
Error output¶
A not-found error (e.g. an unknown ID) prints the following fixed 3-key JSON to stdout when --json is set, and exits with code 1.
{"error": "strategy 'does_not_exist' not found", "code": "strategy_not_found", "id": "does_not_exist"}
| Field | Type | Meaning |
|---|---|---|
error |
string | localized message |
code |
string | stable machine-judgable code (e.g. strategy_not_found) |
id |
string | the resource ID that was not found |
Without --json, a single-line message goes to stderr and exit code is 1 (stdout is empty). Argument errors (Click UsageError) exit with code 2.
Related links¶
- CLI conventions for agents — stdout purity, envelopes, exit codes
- alpha-forge-mcp reference — using the CLI through MCP