get_job_result
Retrieve a completed backtest job's final report and trades, or the strategy comparison output. If the job is not yet complete, it returns an error naming the current state to avoid partial results.
Instructions
Fetch a completed backtest job's final report.
Requires state == "completed"; raises JobNotCompletedError naming
the current state otherwise -- never returns partial results, even if
OctoBot itself would technically serve a report for an in-progress
run (or, for compare_strategies, if some but not all children have
finished). Raises JobNotFoundError for an unknown job_id (same as
get_job_status).
For kind: "backtest", output is {"report": <passthrough>, "trades": <passthrough>} from OctoBot's own GET /backtesting?update_type= backtesting_report response (NFR-8: no renaming/reshaping).
For kind: "compare_strategies" (milestone 10, docs/tasklist.md item
10), output is {"comparison": [...], "diff": ...} -- both already
fully computed and stored (once, at completion -- INV-3) by
_run_compare_strategies as this job's own result, so this function
simply passes them through unchanged; see that function and
_build_comparison_entry/_build_diff below for exactly how each is
built.
Real report/trades shape, captured live against the OctoBot
2.1.1 test instance this milestone (resolves open question #3) --
data_files mode, DailyTradingMode profile, BTC/EUR on
binance, exactly as OctoBot returned it (field names/nesting
verbatim, values are example data from that one run):
{
"report": {
"bot_report": {
"starting_portfolio": {"binance": {"BTC": {"available": 10.0, "total": 10.0}, "USDT": {...}}},
"end_portfolio": {"binance": {"BTC": {...}, "EUR": {...}, "USDT": {...}}},
"profitability": {"binance": -15.223942726263024},
"market_average_profitability": {"binance": -16.839423647357034},
"reference_market": "EUR",
"trading_mode": "DailyTradingMode"
},
"chart_identifiers": [
{"exchange_id": "...", "exchange_name": "binance", "symbol": "BTC/EUR", "time_frames": ["1m", "1h", "4h", "1d"]}
],
"errors_count": 0,
"symbol_report": [{"BTC/EUR": 0.8013392638261951}]
},
"trades": [
{
"SoR": "Simulated", "amount": "7.99127178", "cost": "535066.3103340852",
"date": "2026-05-02 16:00:00", "exchange": "binance", "fee_cost": "535.0663103340852",
"fee_currency": "EUR", "id": "b5eb50fd-...", "market": "EUR", "price": "66956.34",
"ref_market_cost": 535066.3103340853, "symbol": "BTC/EUR", "time": 1777737600,
"type": "SELL MARKET"
},
...
]
}Confirms the spec's own explicit warning was correct: profitability
genuinely exists, but nested three levels deep
(report.bot_report.profitability, itself keyed by exchange name, not
a bare float) -- NOT at the top level of report as a naive read of
the spec's placeholder field list (profitability/max_drawdown/
win_rate) might have assumed; max_drawdown/win_rate were not
observed anywhere in this real payload at all. amount/cost/
fee_cost/price in each trade are numeric-looking strings, not
JSON numbers (ref_market_cost IS a JSON number) -- passed through
exactly as OctoBot sent them (NFR-8), not coerced. This tool does not
hardcode or validate against any of these field names anywhere in its
own code (per NFR-8/the spec's own instruction not to assume a field
list) -- this is documentation of what was observed, not a schema this
tool depends on.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes |