This document defines the formal communication contract between Stepbit-core (external AI/workflow consumer) and QuantLab (research and execution system).
QuantLab is a research-first, CLI-driven system that remains autonomous. This contract provides a stable, machine-readable wrapper for optional headless integration.
Each field and section is labeled with its current implementation status:
[done]— implemented and stable in the current codebase[planned]— defined here but not yet implemented (tracked in linked issues)
The published version of this document lives in
docs/stepbit-io-v1.md. Both files must remain identical.
Stepbit invokes QuantLab as an external consumer via its CLI boundary.
<EXPLICIT_PYTHON_INTERPRETER> main.py --json-request '<JSON_PAYLOAD>'Recommended Configuration Examples:
- Windows:
.venv\Scripts\python.exe - POSIX:
.venv/bin/python
[done] — The --json-request flag is registered and parsed in main.py.
[done] — Runtime resolution has been hardened for local automated execution:
main.pyanchorssrc/intosys.path.PROJECT_ROOTis resolved from the entrypoint.- Default
outdiris anchored to the project root when not explicitly provided.
| Field | Type | Status | Description |
|---|---|---|---|
schema_version |
string |
[done] |
Must be "1.0". Validated by main.py. |
request_id |
string |
[done] |
Caller-generated ID for tracking. Captured as args._request_id. |
command |
string |
[done] |
One of: run, sweep, forward, portfolio. |
params |
dict |
[done] |
Command-specific parameters mapped to CLI args. |
[done] params keys are mapped to argparse namespace attributes via setattr in main.py.
[done] schema_version validation and request_id propagation are implemented.
Required under params:
config_path— non-empty string path to the sweep YAML file
Optional under params:
out_dir— target parent directory for the generated run directorysweep_outdir— legacy-compatible alias forout_dir
Invalid or missing config_path fails deterministically with exit code 2.
QuantLab writes artifacts to a mode-specific output directory upon completion. Stepbit reads results from these files.
QuantLab does not emit a JSON response envelope to stdout. This is by current design.
For command: "run", a successful execution now writes a canonical run directory under
outputs/runs/<run_id>/ and returns the resolved run_id, artifacts_path, and canonical
report_path through the existing session-completion context. Its canonical report.json
now also includes:
machine_contract.schema_version = "1.0"machine_contract.contract_type = "quantlab.run.result"machine_contract.command = "run"machine_contract.statusmachine_contract.request_idwhen providedmachine_contract.run_idmachine_contract.modemachine_contract.summarymachine_contract.artifacts
For plain run, the top-level summary block also mirrors the same core KPI values for compatibility, but report.json.machine_contract remains the canonical machine-facing result surface.
If command: "run" is invoked with params.paper = true, QuantLab now executes through a dedicated paper-session lifecycle and writes artifacts under:
outputs/paper_sessions/<session_id>/
In that case:
- the external request contract still remains
command = "run" - lifecycle signalling still uses
mode = "run"for compatibility - the produced
report.json.machine_contractidentifies the result asquantlab.paper.result - the returned
run_idis the paper session identifier
Canonical paper-session artifacts currently include:
session_metadata.jsonsession_status.jsonconfig.jsonmetrics.jsonreport.jsontrades.csvrun_report.md
For command: "sweep", the canonical machine-readable artifact is report.json, and it includes:
machine_contract.schema_version = "1.0"machine_contract.contract_type = "quantlab.sweep.result"machine_contract.command = "sweep"machine_contract.statusmachine_contract.request_idwhen providedmachine_contract.run_idmachine_contract.modemachine_contract.summarymachine_contract.best_resultwhen availablemachine_contract.artifacts
A future version may emit a structured JSON envelope to stdout on completion.
Example success shape:
{
"schema_version": "1.0",
"request_id": "req_550e8400",
"status": "success",
"run_id": "20260320_162100_run_a1b2c3d",
"artifacts_path": "outputs/runs/20260320_162100_run_a1b2c3d",
"summary": {
"total_return": 0.45,
"sharpe_simple": 1.82,
"max_drawdown": -0.15,
"trades": 12,
"win_rate": 0.62
}
}Example failure shape:
{
"schema_version": "1.0",
"request_id": "req_550e8400",
"status": "error",
"error": {
"code": "DATA_ERROR",
"message": "OHLC data missing for requested range"
}
}[planned] Response envelope tracked in Issue #22.
QuantLab supports optional, file-based session signalling to notify Stepbit of lifecycle events without polling.
python main.py --json-request '...' --signal-file path/to/signals.jsonl- Format: JSON Lines (one JSON object per line).
- Mode: Append-only.
- Reliability: Best-effort writes; signal failures do not abort the session.
| Field | Type | Description |
|---|---|---|
schema_version |
string |
Always "1.0". |
event |
string |
SESSION_STARTED, SESSION_COMPLETED, or SESSION_FAILED. |
status |
string |
running, success, or error. |
mode |
string |
The public command type (e.g., run, sweep). |
request_id |
string |
Propagated from request if available. |
timestamp |
string |
ISO 8601 local time. |
Compatibility note:
command: "run"withpaper = truestill emitsmode = "run"in signals so external consumers do not experience a breaking contract change- paper-specific lifecycle can be inferred from the returned artifact path under
outputs/paper_sessions/and fromreport.json.machine_contract.contract_type = "quantlab.paper.result"
Includes result location metadata (when available):
run_id: Unique identifier for the run.artifacts_path: Directory containing the run artifacts.report_path: Path to the canonicalreport.json.runs_index_json: Refreshed registry artifact foroutputs/runs/after successfulrun,sweep, andforward.
For paper-backed run executions:
run_idis the paper session identifierartifacts_pathpoints tooutputs/paper_sessions/<session_id>/runs_index_jsonis not expected because paper sessions do not currently refresh the shared run registry
Includes failure metadata:
exit_code: Numeric process exit code (1-4).error_type: Exception class name.message: Human-readable error description.
| Code | Label | Meaning |
|---|---|---|
0 |
SUCCESS |
Task completed normally. |
1 |
GENERAL_ERROR |
Unexpected crash or unhandled exception. |
2 |
INVALID_CONFIG |
JSON payload or CLI flags are invalid. |
3 |
DATA_ERROR |
OHLC data missing or invalid state (e.g. empty or unusable data). |
4 |
STRATEGY_ERROR |
Strategy-specific logic failure or parameter/runtime error. |
The canonical machine-readable artifact for integration is report.json.
For session-oriented flows, it is expected inside the produced run/session directory.
- Typical pattern:
outputs/runs/<run_id>/report.json
Canonical run artifact set for new run- and sweep-produced runs:
outputs/runs/<run_id>/metadata.jsonoutputs/runs/<run_id>/config.jsonoutputs/runs/<run_id>/metrics.jsonoutputs/runs/<run_id>/report.json
Successful run, sweep, and forward executions refresh the shared registry:
outputs/runs/runs_index.csvoutputs/runs/runs_index.jsonoutputs/runs/runs_index.md
Legacy meta.json and run_report.json remain read-compatible only.
Paper-backed run executions currently use a distinct session root:
outputs/paper_sessions/<session_id>/
session_metadata.json
session_status.json
config.json
metrics.json
report.json
trades.csv
run_report.md
These paper-session artifacts are operationally distinct from the research run registry under outputs/runs/.
QuantLab provides machine-verifiable flags for runtime validation.
| Flag | Status | Description |
|---|---|---|
--version |
[done] |
Prints the current QuantLab version as a stable string. |
--check |
[done] |
Prints a deterministic JSON health summary and exits 0 on success or 2 on runtime/config failure. |
--check currently reports:
statusproject_rootmain_pathsrc_rootinterpretervenv_activequantlab_importpython_versionversion
| Gap | Issue |
|---|---|
| JSON response envelope emitted to stdout | #22 |
strategy param mapping in run command |
#21 |
| Webhook delivery for signals | #25 (Deferred) |