Skip to main content

Endpoints

MethodEndpointDescription
GET/v1/positionsGet current positions
POST/v1/positions/balanceGet single account balance
POST/v1/positions/balancesGet multiple account balances
GET/v1/positions/ledgerQuery historical position changes. See Position Ledger.
GET/v1/positions/ledger/downloadDownload position ledger as CSV. See Position Ledger.

Position Data

Each position includes:
FieldDescription
symbolTrading instrument
accountTrading account
netPositionCurrent net position quantity
qtyBoughtTotal quantity bought
qtySoldTotal quantity sold
costTotal cost basis (scaled integer)
realizedRealized profit/loss (scaled integer)
bodPositionBeginning-of-day position
updateTimeLast update timestamp

Balance Data

Account balances include:
FieldDescription
balanceCurrent cash balance
buyingPowerAvailable buying power for trading
capitalRequirementRequired capital to maintain positions
excessCapitalCapital above requirements
marginRequirementMargin required for open positions
unsettledFundsFunds pending settlement
openOrdersValue reserved for open orders
updateTimeLast update timestamp

Historical Position Queries

Query positions as they existed at a specific point in time. Useful for regulatory reporting, reconciliation, or end-of-day snapshots.

Parameters

ParameterTypeDescription
as_of_timeRFC3339 timestampExact point-in-time (e.g., 2026-01-02T17:00:00Z)
as_of_dateDate objectEnd-of-trading-day snapshot (year, month, day)
Mutually Exclusive: Use as_of_time OR as_of_date, not both. The as_of_time parameter already contains date information.

Examples

Query by timestamp (exact point in time):
GET /v1/positions?name=firms/ISV-Alice/accounts/trading&as_of_time=2026-01-02T17:00:00Z
Query by trade date (end of trading day):
GET /v1/positions?name=firms/ISV-Alice/accounts/trading&as_of_date.year=2026&as_of_date.month=1&as_of_date.day=2

Use Cases

  • End-of-Day Reports: Query positions at market close for daily P&L calculations
  • Regulatory Snapshots: Capture position state at specific regulatory timestamps
  • Reconciliation: Compare historical positions against external records
  • Audit Trail: Review position history for compliance or dispute resolution

Usage Notes

Position Updates After TradesPosition data is updated after each trade execution. For real-time trade notifications that affect positions, use the gRPC Order Stream.
  • Positions are aggregated by symbol and account
  • Balances reflect current available and reserved amounts
  • Use the Order Stream for real-time execution updates that affect positions
  • Historical queries return the position state as of the specified time

Position Ledger

The position ledger records every change to a position as a single entry, with both the delta (quantityChange, costChange, realizedChange) and the cumulative state immediately after the change (netPosition, cost, realized). Use it for reconciliation, point-in-time replay, and end-of-day reporting.

Ledger Endpoints

MethodEndpointgRPCRequired ScopeDescription
GET/v1/positions/ledgerPositionAPI.GetPositionLedgerread:positionsPaginated query of position ledger entries
GET/v1/positions/ledger/downloadPositionAPI.DownloadPositionLedgerread:positionsStreamed CSV download of position ledger entries
Both endpoints are scoped under read:positions (the same scope used for GetAccountBalance, ListAccountBalances, and the position queries on this section). Calls without read:positions fail with 403 Forbidden (REST) / PERMISSION_DENIED (gRPC).

Historical Floor

SettingValue
Earliest queryable date2026-05-01T00:00:00Z
The ledger has a hard historical floor of May 1, 2026 (UTC). Enforcement is defense-in-depth:
  1. start_time is clamped upstream to the floor when the caller asks for earlier data.
  2. Entries with update_time before the floor are post-filtered from responses.
Pre-floor entries are not retrievable through this endpoint.

Delta Computation

Each ledger entry is computed from the exchange’s before/after position snapshots:
quantityChange = after.netPosition - before.netPosition
costChange     = after.cost        - before.cost
realizedChange = after.realized    - before.realized
The cumulative fields (netPosition, cost, realized) on each entry are the state immediately after the change. To reconstruct point-in-time position state, replay entries in update_time order.

Cross-Firm Access

The account in account=firms/{firm}/accounts/{id} must belong to the caller’s firm (extracted from the JWT firm_id claim). Cross-firm access is blocked at the gateway:
ConditionError Code
Account belongs to a different firmPermissionDenied
JWT missing or firm_id absentUnauthenticated
ISV credentials not configured at gatewayFailedPrecondition
Upstream exchange service unavailableUnavailable

Get Position Ledger

GET /v1/positions/ledger?account=firms/ISV-Alice/accounts/alice-trading&start_time=2026-05-01T00:00:00Z&page_size=100

Query Parameters

ParameterTypeRequiredDescription
accountstringYesFully qualified account name. Example: firms/ISV-Alice/accounts/alice-trading.
symbolstringNoFilter by instrument symbol.
start_timeRFC3339NoInclusive lower bound on update_time. Clamped to 2026-05-01T00:00:00Z.
end_timeRFC3339NoInclusive upper bound on update_time.
page_sizeintegerNoMaximum entries per page. Max 1000; values above 1000 return InvalidArgument.
page_tokenstringNoPagination token from a previous response’s nextPageToken.
newest_firstbooleanNoIf true, descending update_time order. Default false.

Sample Response

{
  "entries": [
    {
      "id": "pl_01HXYZ...",
      "account": "firms/ISV-Alice/accounts/alice-trading",
      "symbol": "tec-nfl-sbw-2026-02-08-kc",
      "quantityChange": "50",
      "costChange": "26000",
      "realizedChange": "0",
      "netPosition": "150",
      "cost": "78000",
      "realized": "0",
      "updateTime": "2026-05-02T14:30:15.123Z",
      "updateBusinessDate": "2026-05-02",
      "description": "Trade fill"
    }
  ],
  "nextPageToken": "eyJvZmZzZXQiOjEwMH0=",
  "eof": false
}

PositionLedgerEntry Fields

FieldTypeDescription
idstringUnique entry identifier.
accountstringAccount this entry belongs to.
symbolstringInstrument symbol.
quantityChangeint64Delta from previous position (after.netPosition - before.netPosition).
costChangeint64Delta in cost basis.
realizedChangeint64Delta in realized P&L.
netPositionint64Net position after this change.
costint64Cost basis after this change.
realizedint64Cumulative realized P&L after this change.
updateTimeRFC3339Timestamp of the position change.
updateBusinessDatestringBusiness date in YYYY-MM-DD.
descriptionstringHuman-readable reason (e.g., trade fill, expiry, correction).
int64 fields are serialized as strings in JSON (e.g., "50" not 50) per the protobuf JSON mapping spec. Parse them as strings to avoid precision loss in languages with 53-bit integer limits.

Response Wrapper Fields

FieldTypeDescription
entriesarrayPosition ledger entries for the requested account / time window.
nextPageTokenstringPagination token to fetch the next page. Empty when there are no more results.
eofbooleantrue when this response contains the final page of results.

Download Position Ledger

GET /v1/positions/ledger/download?account=firms/ISV-Alice/accounts/alice-trading&start_time=2026-05-01T00:00:00Z
The download endpoint accepts the same query parameters as GET /v1/positions/ledger (account, symbol, start_time, end_time, page_size, page_token, newest_first). It streams raw CSV bytes from the upstream ledger as a passthrough; the gateway does not parse individual rows.
CSV passthrough caveat. Because the body is opaque to the gateway, ledger entries cannot be post-filtered row-by-row. The historical floor is still enforced upstream via start_time clamping, but no per-row filtering is performed on the CSV stream.
Empty result sets return a single empty chunk followed by EOF (HTTP 200 with an empty body). The gateway translates an upstream “stream returned a nil response” condition into an empty result.

Ledger Rate Limits (per firm)

EndpointRateBurstEffective
GET /v1/positions/ledger0.5 req/sec5~30 req/min
GET /v1/positions/ledger/download0.0833 req/sec1~5 req/min
Exceeding these limits returns ResourceExhausted (429 Too Many Requests).

Ledger Error Codes

ErrorCause
InvalidArgumentMissing account.
PermissionDeniedAccount belongs to a different firm.
UnauthenticatedMissing JWT or firm_id claim.
FailedPreconditionISV credentials not configured.
UnavailableUpstream exchange service not connected.
ResourceExhaustedPer-firm rate limit exceeded.

See Also

Balance Ledger

Cash balance changes (deposits, withdrawals, fills, fees)

Balance Ledger Stream

Real-time balance ledger via gRPC

Order Stream

Real-time execution updates that affect positions

Authentication

Required scopes and OAuth flow