---
title: "Get Performance History"
method: GET
path: "/performance/history"
tags: ["Performance"]
---

# Get Performance History

`GET /performance/history`

## Request

- Base URL: `http://localhost:8000`
- URL: `http://localhost:8000/performance/history`
- Auth: HTTP basic

Historical performance for one subject, newest first, in one normalized row shape.

Both subjects page identically: descending timestamp, `cursor` is the last row's
timestamp, `has_more` says whether another page exists.

`interval` is a floor, not a guarantee. The controller series is written on a
5-minute grain, so asking it for `1m` returns that native grain; the executor series
is written on PERFORMANCE_EXECUTOR_SNAPSHOT_INTERVAL (60s by default). The echoed
`interval` says what was asked for, the timestamps say what was served.

It is a claim about resolution only. Sampling is per scope -- per executor, per (bot,
controller) -- so a coarser interval thins every scope's own series and never drops a
scope from the answer. An unnarrowed query over a fleet returns the whole fleet at
every interval, at a lower resolution.

Realized vs unrealized, for executors: an executor reports one net PnL, and the split
comes from whether its position is really settled. A `POSITION_HOLD` close is NOT
settled -- it hands the position on to position_holds, which report it in their own
right -- so its PnL stays in `unrealized_pnl_quote` even though `is_terminal` is true
and `status` is TERMINATED. Counting it realized here would double-count it, the same
exclusion /executors/performance applies. Any other terminal close is realized.

An executor's series is answered from executor_performance_snapshots alone, including
its final value: completion writes a terminal row, so there is no join to the
executors table and no "and then append the last point" rule. An executor that was
live when the API crashed gets its terminal row from the startup reap instead, carrying
`close_type: SYSTEM_CLEANUP` and the last figures observed before the crash -- an
approximated close, marked as one, rather than a series that never ends.

## Query parameters

- `subject` string, required: Which population to read: "controller" or "executor"
- `bot_name` string, nullable: Filter by bot name (controller subject only)
- `controller_id` string, nullable: Filter by controller ID (either subject)
- `executor_id` string, nullable: Filter by executor ID (executor subject only)
- `executor_type` string, nullable: Filter by executor type (executor subject only)
- `account_name` string, nullable: Filter by account name (executor subject only)
- `connector_name` string, nullable: Filter by connector (executor subject only)
- `trading_pair` string, nullable: Filter by trading pair (executor subject only)
- `start_time` string, nullable: ISO 8601 start of the window
- `end_time` string, nullable: ISO 8601 end of the window
- `interval` string
- `limit` integer
- `cursor` string, nullable: Cursor from a previous page's next_cursor

## Response `200`

Successful Response

- PerformanceHistoryResponse: Envelope of GET /performance/history, matching the controller route's shape.
  - `status` string
  - `data` PerformanceRow[], required
    - `timestamp` string, required: ISO 8601 timestamp of the snapshot
    - `subject` string, required: Which population this row came from: "controller" or "executor"
    - `scope_id` string, required: controller_id for controllers, executor_id for executors
    - `status` string, required: Status at the moment of the snapshot
    - `is_terminal` boolean: True only on the final row of a completed executor's series. Always false for controllers, which have no terminal row.
    - `realized_pnl_quote` number, required: Realized PnL in quote currency. For an executor this is its net PnL once the position is really settled, and 0 while it is not. A POSITION_HOLD close is not settled: the position carries on in position_holds, so a held executor's PnL stays unrealized here even though is_terminal is true.
    - `unrealized_pnl_quote` number, required: Unrealized PnL in quote currency. For an executor this is its net PnL while the position is open, and after a POSITION_HOLD close.
    - `global_pnl_quote` number, required: Total PnL (realized + unrealized) in quote currency
    - `global_pnl_pct` number, required: Total PnL as a fraction of the capital deployed
    - `volume_quote` number, required: Volume traded in quote currency. Volume GENERATED, not capital deployed: an LP position's deposit is excluded.
    - `cum_fees_quote` number, nullable: Cumulative fees in quote currency. NULL for controllers: their PerformanceReport genuinely has no fees field, and unknown is not zero.
    - `bot_name` string, nullable: Docker bot name; controllers only
    - `controller_id` string, nullable: Controller ID, on both subjects
    - `executor_id` string, nullable: Executor ID; executors only
    - `executor_type` string, nullable: Executor type; executors only
    - `account_name` string, nullable: Account name; executors only
    - `connector_name` string, nullable: Connector name; executors only
    - `trading_pair` string, nullable: Trading pair; executors only
    - `close_type` string, nullable: Close type; set on an executor's terminal row
    - `performance` object: Controllers: the raw stored PerformanceReport. Empty for executors, whose metrics are all typed columns above.
    - `custom_info` object: Controllers: the raw stored custom_info. Empty for executors -- these payloads carry fill_events and grid levels, which have no business in a per-minute row.
  - `pagination` object, required: next_cursor, has_more, limit and the interval that was requested

## Other responses

- `422`: Validation Error

## Changes

- **2026-10-14** `66c2e6c1da2e`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/hummingbot/apis/hummingbot-api/changes/performance/history/get.md)

---

[API](https://skmtc.dev/hummingbot/apis/hummingbot-api.md) - [All operations](https://skmtc.dev/hummingbot/apis/hummingbot-api/llms.txt) - [OpenAPI document](https://skmtc.dev/hummingbot/apis/hummingbot-api/revisions/66c2e6c1da2e?raw)
