Skip to main content
These methods return authoritative calculations computed in Core — the same numbers across every client. They are application-scoped and require authentication. getSummary, getPerformance, getRealizedPnl, getAssets, and the two sdk.profile methods below read from a dedicated replica, so they may reflect replica lag and can trail a live position or balance read by a short interval. Do not reconcile a number from this page against a live one to the cent. A reader outage returns a retryable 503 (gRPC Unavailable) rather than falling back to the writer, so treat unavailability as transient and retry. getMargin() is the exception: it is writer-backed rather than replica-backed, as is trade history. Its withdrawable-collateral bounds are still not unconditionally live, and the fallback has two scopes: if the matching engine serves no bounds at all, the account total and every risk bucket row fall back to a persisted reconstruction; if it serves bounds but one bucket is missing, only that row falls back. The reconstruction uses the same formulas the withdrawal gates enforce, but cannot see their resting-order reserves or the parent margin account’s live allocation constraint, so treat a fallback value as an estimate rather than an enforceable bound. Do not make these analytics the sole input to a real-time control of your own — a withdrawal gate, a risk limit, or a liquidation decision; read the live surfaces for that. Historical methods accept an optional period of "1d", "1w", "1m", or "all", defaulting to all when omitted. getMargin() is current-only and takes no period. All decimal amounts and percentages are returned as strings. Every response carries a window (or asOf) describing the query cutoff and how much history the retained data actually covers. Dependent values that cannot be computed — a return over a window with no starting equity, or the value of an asset with no spot price — are omitted rather than reported as 0; branch on their presence before displaying. Two shared shapes appear across these responses:
  • PortfolioWindow — period, asOf (query cutoff, ISO 8601), optional dataThrough / requestedStartsAt / effectiveStartsAt / historyStartsAt, and coverage ("complete", "partial", or "none").
  • PortfolioSeriesWindow — extends PortfolioWindow with displayInterval ("5m"/"1h"/"4h"/"1d") and calculationInterval ("1m"/"5m"/"15m"/"1h"/"4h"/"1d").
valuationStatus is "complete" when every input had a price and "partial" when at least one dependent value was omitted for a missing price.
(params?: { period }) => Promise<PortfolioSummary>
Return the current equity breakdown plus trading activity over the requested window.Requires authentication.Parameters: PortfolioPeriodParams (optional):
  • period: "1d" | "1w" | "1m" | "all" — defaults to all
Returns: PortfolioSummary:
  • window: PortfolioWindow
  • valuationStatus: "complete" | "partial"
  • current: current equity split — perpsEquity (string), plus optional totalEquity, spotEquity, unrealizedPnl, and availableValue
  • activity: period activity — volume (string), tradeCount (number), orderCount (number), feesPaid (string)
Example:
(params?: { period }) => Promise<PortfolioPerformance>
Return transfer-neutral dollar PnL and a sampled time-weighted return (TWR) series for the window. Deposits and withdrawals are excluded from PnL so external flows do not distort return.For any period with a performance baseline, unpriceable external withdrawals are omitted from withdrawals and the dependent deposits total; successful responses with these omissions report valuationStatus: "partial". A recognized quote asset without a valuation pair is valued at 1, so it is not omitted for lacking a pair. Reconciliation checks still apply: if derived deposits become negative, the request fails as unavailable.Requires authentication.Parameters: PortfolioPeriodParams (optional):
  • period: "1d" | "1w" | "1m" | "all" — defaults to all
Returns: PortfolioPerformance:
  • window: PortfolioSeriesWindow
  • calculationMethod: "sampled_twr"
  • valuationStatus: "complete" | "partial"
  • startingEquity?, endingEquity?: string
  • deposits, withdrawals, netExternalFlows: string
  • pnl?, returnPct?: string — omitted when the window lacks a starting equity to measure against
  • maxDrawdown?: { amount, pct, peakAt, troughAt } — positive loss magnitude, peak-to-trough sampled-TWR percentage, and the peak/trough timestamps
  • points: PortfolioPerformancePoint[] — each { timestamp, equity, cumulativePnl, cumulativeReturnPct? }
Example:
(params?: { period }) => Promise<PortfolioRealizedPnl>
Return realized PnL for the window, split by source. Spot-withdrawal disposal PnL is excluded.Requires authentication.Parameters: PortfolioPeriodParams (optional):
  • period: "1d" | "1w" | "1m" | "all" — defaults to all
Returns: PortfolioRealizedPnl:
  • window: PortfolioSeriesWindow
  • bucketInterval: "1h" | "1d" — the series bucket size (hourly for 1d, daily otherwise)
  • totals: PortfolioRealizedTotals — perpsClosedPnl, spotTradePnl, fundingPnl, feesPaid, netRealizedPnl (all strings)
  • closedTrades: { count, winners, losers, breakEven, positions, winRatePct? } — winners, losers, breakEven, and winRatePct are position-based: each position (one flat→open→flat round-trip) is classified once by its net realized PnL after all fees, not per closing fill. positions is the total classified positions (winners + losers + breakEven); count stays fill-based (closing fills in the window)
  • bestDay?: { date, netRealizedPnl } — date is a UTC YYYY-MM-DD calendar day
  • points: PortfolioRealizedPnlPoint[] — the totals fields plus a timestamp, one per bucket
Example:
(params?: { period }) => Promise<PortfolioAssets>
Return per-asset spot holdings priced by asset ID, with allocation percentages and window-scoped spot performance. Rows for assets without a spot price are returned unpriced, with value, allocationPct, and markPrice omitted.Requires authentication.Parameters: PortfolioPeriodParams (optional):
  • period: "1d" | "1w" | "1m" | "all" — defaults to all
Returns: PortfolioAssets:
  • window: PortfolioSeriesWindow
  • valuationStatus: "complete" | "partial"
  • totalSpotEquity?: string — omitted when any held asset is unpriced
  • performance: { pnl?, returnPct?, calculationMethod: "sampled_twr" }
  • assets: PortfolioAssetRow[] — each { assetId, symbol, totalBalance, availableBalance, lockedBalance, markPrice?, value?, allocationPct?, priceAsOf?, pricingStatus, avgCost?, unrealizedPnl?, unrealizedRoe?, cumRealized? } where pricingStatus is "priced" or "unpriced". The spot cost-basis fields — avgCost (weighted-average cost per unit), unrealizedPnl (costBasisQuantity × (markPrice − avgCost)), unrealizedRoe (percentage return on cost basis), and cumRealized (lifetime realized PnL, gross of fees) — are present when the asset has a cost-basis checkpoint; unrealizedPnl and unrealizedRoe additionally require a mark price, and unrealizedRoe additionally requires a non-zero cost basis
Example:
(params?: PortfolioMarginParams) => Promise<PortfolioMargin>
Return the current margin picture: parent-account totals and per-risk-bucket projections, kept separate. Parent free collateral does not include risk-bucket free collateral. This method is current-only and takes no period. By default it describes the default margin account; pass marginAccountId to describe one of your sub-accounts instead, and includeAccounts to add the owner rollup across every open margin account.Requires authentication.Parameters: PortfolioMarginParams (optional):
  • marginAccountId?: string — one of your margin accounts (the default one or a sub-account) to describe instead of the default one; total, riskBuckets and the positions counts are then that account’s own. Without it, total and riskBuckets describe the default margin account while the positions counts stay owner-wide across every margin account
  • includeAccounts?: boolean — also return accounts and ownerTotal
Returns: PortfolioMargin:
  • asOf: string — snapshot timestamp (ISO 8601)
  • marginAccountId?: string — the margin account total and riskBuckets describe (and the positions counts too, when the request names marginAccountId; otherwise those stay owner-wide); absent before your first margin account exists
  • accounts?: PortfolioMarginAccount[] — with includeAccounts: every open margin account, the default one first, then sub-accounts by key; each { marginAccountId, subaccountKey, total }, where subaccountKey is "default" for the default margin account and total is a PortfolioMarginTotal whose withdrawableCollateral is the persisted reconstruction of the engine’s bound
  • ownerTotal?: PortfolioMarginTotal — with includeAccounts: the sum of accounts’ totals
  • total: PortfolioMarginTotal — equity, collateralBalance, parentAvailableCollateral (parent-only free collateral), withdrawableCollateral, initialMarginRequired, maintenanceMarginRequired, unrealizedPnl (all strings)
  • positions: { total, cross, isolated } — open-position counts
  • riskBuckets: PortfolioRiskBucket[] — each { riskBucketId, marginMode, tradingPairId?, collateral, freeCollateral, withdrawableCollateral, maxRemovableCollateral?, availableOrderCollateral?, initialMarginRequired, maintenanceMarginRequired, unrealizedPnl, positionCount, copyFollowId?, copyLeaderHandle? }. availableOrderCollateral is the engine-served capacity for new orders in that bucket (parent auto top-up included, not additive across rows — see the margin-accounts docs for the full semantics); it is present only on live-engine reads, so handle undefined. maxRemovableCollateral is the largest removal the engine accepts when it may also lower the posted margin of an isolated bucket’s one open position (equal to withdrawableCollateral otherwise), also live-engine only. marginMode ("ISOLATED" / "CROSS") is always present and lets you attribute bucket margin to cross or isolated without a second call; tradingPairId is present only on ISOLATED rows, since a cross bucket spans the pairs selected into it and is scoped to none individually (read that membership from the margin-accounts selectedTradingPairIds). copyFollowId and copyLeaderHandle are present only on a copy risk bucket row — the follow that funds it and the followed leader’s custom TraderCode handle, when still held — and omitted on every other row.
marginMode does not reconcile with the positions.cross / positions.isolated counts, and grouping riskBuckets by mode will not reproduce them. Without marginAccountId, those counts are computed by a separate query over every margin account for your user/application pair, while riskBuckets comes from the single oldest (parent) account, and they attribute a legacy position carrying no risk-bucket reference to cross whether or not a cross bucket row exists. Treat them as account-wide totals, not as a checksum over the rows. For the same reason these labels are not a join key against the margin-accounts surface — that surface emits rows this one has no counterpart for, and the two disagree on legacy-position attribution on persisted-fallback reads.
parentAvailableCollateral is an equity figure: it credits settled realized PnL, accrued funding, and unrealized gains, none of which are principal a transfer can move. The bound the engine actually enforces on transfers-out and withdrawals — and, because allocating collateral to a risk bucket passes the same principal-gated door, the effective ceiling on funding a new isolated or first-use position — is total.withdrawableCollateral. The two can differ without limit (e.g. a large banked realized PnL lifts parentAvailableCollateral far above what any transfer would accept). Size transfers, withdrawals, and new isolated/first-use positions from withdrawableCollateral; treat parentAvailableCollateral/freeCollateral as trading-margin headroom for existing cross exposure.Example:
Throws: 400 when marginAccountId is not a UUID; 404 when it is not one of your margin accounts; 401 without authentication; 429 past the read budget; 503 when portfolio data is unavailable; 500 on an internal error.

Aggregate stats and chart (sdk.profile)

Two earlier portfolio methods live under the sdk.profile namespace. They return a single aggregate snapshot and a simple time-bucketed series, and remain supported. Prefer the authoritative sdk.portfolio methods above for richer breakdowns and explicit history coverage. The v1.0.21 fee-total corrections apply to both methods below.
(period: PortfolioPeriod) => Promise<PortfolioStats>
Fetch aggregate trading statistics for the authenticated user over a given time period.Parameters:
  • period: "24h" | "7d" | "30d" | "all"
Returns:
  • pnl: Average-cost realized PnL over the period (quote asset, normalized) — net of fees, across spot and perps incl. funding; excludes unrealized PnL. Spot withdrawals are booked as fair-value disposals (last trade price − avg cost) × qty; deposits and withdrawals are priced at the last observed trade price in event order; transfers that predate any trade on the pair are PnL-neutral; short-entry fees reduce short profits
  • volume: Total trading volume (quote asset, normalized)
  • totalTrades: Total number of trades executed
  • totalOrders: Total number of orders placed
  • feesPaid: Total fees paid (quote asset, normalized)
  • totalEquity: Total equity (quote asset, normalized) — spot balances plus perpetuals (margin) account equity
  • spotEquity: Spot account equity (quote asset, normalized)
  • perpsEquity: Perpetuals (margin) account equity (quote asset, normalized)
  • winLossRatio: Position-level win rate — winning positions over winning + losing positions, break-even excluded (number); each position (one flat→open→flat round-trip) is classified once by its net realized PnL after all fees, not per fill; null when the period has no closed positions
  • maxDrawdown: Peak-to-trough drawdown on running PnL; null when the period has no closed trades
(period: PortfolioPeriod, metric: PortfolioMetric) => Promise<PortfolioChartResponse>
Fetch a time-bucketed chart series for a given period and metric.Parameters:
  • period: "24h" | "7d" | "30d" | "all"
  • metric: "volume" | "pnl"
Returns:
  • metric: The metric returned
  • period: The period requested
  • data: Array of { timestamp: string, value: string } data points (ISO 8601 bucket start times)