Skip to content

--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 code 0.
  • 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.