Trades
The Trade endpoint returns the fill history for an account — every executed order that resulted in a position change. Use this to analyze your strategy’s trading activity, calculate performance metrics, or build a trade journal.
Trades vs. Orders
- Orders are instructions to buy or sell (may be pending, partially filled, filled, or canceled)
- Trades (also called “fills”) are the actual executions — completed transactions where contracts changed hands
An order with status 2 (Filled) will have a corresponding trade. Pending and canceled orders do not generate trades.
POST Trade/search
Get the trade/fill history for an account. Returns all trades executed during the current session.
URL: POST /api/sim/Trade/search
Authentication: Bearer token required.
Request body
{ "accountId": 12345, "startTimestamp": "2025-01-15T00:00:00Z", "endTimestamp": "2025-01-16T00:00:00Z"}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
startTimestamp | string | No | Only return fills at or after this time |
endTimestamp | string | No | Only return fills at or before this time |
Response
Returns up to 500 fills, newest first.
{ "success": true, "errorCode": 0, "errorMessage": null, "trades": [ { "id": 2, "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "creationTimestamp": "2025-01-15T14:45:12.000Z", "price": 21518.50, "profitAndLoss": null, "fees": 1.24, "side": 1, "size": 1, "voided": false, "orderId": 1000000043 }, { "id": 1, "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "creationTimestamp": "2025-01-15T14:30:05.000Z", "price": 21503.25, "profitAndLoss": null, "fees": 1.24, "side": 0, "size": 1, "voided": false, "orderId": 1000000042 } ]}| Field | Type | Description |
|---|---|---|
trades | array | List of trade/fill objects, newest first |
trades[].id | number | Trade index within the response |
trades[].accountId | number | Account ID |
trades[].contractId | string | Contract ID |
trades[].creationTimestamp | string | ISO 8601 timestamp of the fill |
trades[].price | number | Execution price |
trades[].profitAndLoss | null | Always null — fills are half-turns; compute round-trip P&L yourself |
trades[].fees | number | Commission/fees charged on the fill |
trades[].side | number | 0 = Buy, 1 = Sell |
trades[].size | number | Number of contracts filled |
trades[].voided | boolean | Always false |
trades[].orderId | number | The order ID that generated this trade |
Example
result = api("Trade/search", {"accountId": ACCOUNT_ID})
if result["success"]: trades = result.get("trades", []) print(f"Total trades: {len(trades)}")
for trade in trades: side = "BUY" if trade["side"] == 0 else "SELL" print(f" {trade['creationTimestamp']} — {side} {trade['size']} @ {trade['price']}")curl -X POST https://api.test-max.com/api/sim/Trade/search \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"accountId": 12345}'Calculating performance from trades
You can compute key performance metrics directly from the trade history:
Total P&L
This FIFO example supports long and short positions, partial exits, and reversals. Use a complete fill history starting from a flat position, for one contract, with TICK_VALUE and TICK_SIZE set for that contract. Trade/search returns at most 500 fills: a truncated response or a timestamp filter that omits entries cannot produce reliable P&L. For a full run, use the final account balance from Replay/end minus the starting balance for net realized P&L after positions are flattened, assuming no other balance adjustments.
from collections import deque
result = api("Trade/search", {"accountId": ACCOUNT_ID})if not result["success"]: raise RuntimeError(result.get("errorMessage"))trades = sorted(result.get("trades", []), key=lambda t: t["creationTimestamp"])entries = deque()round_trips = []gross_pnl = 0.0fees = sum(trade["fees"] for trade in trades)
for trade in trades: direction = 1 if trade["side"] == 0 else -1 remaining = trade["size"] while remaining and entries and entries[0]["direction"] != direction: entry = entries[0] quantity = min(remaining, entry["remaining"]) pnl = (trade["price"] - entry["price"]) * entry["direction"] * quantity * TICK_VALUE / TICK_SIZE gross_pnl += pnl round_trips.append(pnl) remaining -= quantity entry["remaining"] -= quantity if entry["remaining"] == 0: entries.popleft() if remaining: entries.append({"direction": direction, "price": trade["price"], "remaining": remaining})
net_pnl = gross_pnl - feesprint(f"Gross realized P&L: ${gross_pnl:,.2f}")print(f"Fill fees: ${fees:,.2f}")print(f"Net realized P&L after all fill fees: ${net_pnl:,.2f}")Gross realized P&L excludes fees. Net realized P&L subtracts all returned fill fees, including fees on any still-open entries; neither value includes unrealized P&L on those entries. When all positions are closed, net P&L includes both entry and exit fees.
Win rate of matched exit portions
Reuse round_trips from the FIFO example above. Each matched exit portion counts once, so scaling out can produce several results for one position. This is a gross win rate before fees, rather than the app’s closed-position win rate.
wins = sum(pnl > 0 for pnl in round_trips)losses = sum(pnl < 0 for pnl in round_trips)total_trades = wins + losseswin_rate = (wins / total_trades * 100) if total_trades > 0 else 0print(f"Gross win rate of matched exit portions: {win_rate:.1f}% ({wins}W / {losses}L)")Trade count and frequency
from datetime import datetime
trades = sorted(result.get("trades", []), key=lambda t: t["creationTimestamp"])if len(trades) >= 2: first = datetime.fromisoformat(trades[0]["creationTimestamp"].replace("Z", "+00:00")) last = datetime.fromisoformat(trades[-1]["creationTimestamp"].replace("Z", "+00:00")) duration = last - first print(f"Session duration: {duration}") print(f"Trades: {len(trades)} over {duration}")Usage notes
- Trades are returned newest first (up to 500). Sort by
creationTimestampif you need chronological order. - Each fill is a separate trade object, even if the order was partially filled across multiple prices.
- The trade list is scoped to the current replay session. Starting a new session resets the trade history.
- Use
Trade/searchafter callingReplay/endto get the complete trade log for a session.Replay/endreturns only the final account snapshot (balance and equity), whileTrade/searchgives you the individual fill details.