Positions
Position endpoints let you query currently open positions, close an entire position, or partially close a position. Positions are created automatically when orders are filled and represent your current exposure in a given instrument.
Position fields
| Field | Type | Description |
|---|---|---|
accountId | number | Account ID |
contractId | string | Contract ID for the position |
type | number | Position direction: 1 = Long, 2 = Short |
size | number | Number of contracts currently held |
averagePrice | number | Average entry price across all fills |
POST Position/searchOpen
Get all currently open positions for an account.
URL: POST /api/topstep-sim/Position/searchOpen
Authentication: Bearer token required.
Request body
{ "accountId": 12345}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
Response
{ "success": true, "errorCode": 0, "errorMessage": null, "positions": [ { "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "type": 1, "size": 2, "averagePrice": 21503.50 } ]}| Field | Type | Description |
|---|---|---|
positions | array | List of open position objects. Empty array [] if no positions are open. |
Example
result = api("Position/searchOpen", {"accountId": ACCOUNT_ID})
positions = result.get("positions", [])if positions: for pos in positions: direction = "LONG" if pos["type"] == 1 else "SHORT" print(f"{direction} {pos['size']} @ {pos['averagePrice']:.2f}")else: print("No open positions")curl -X POST https://api.test-max.com/api/topstep-sim/Position/searchOpen \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"accountId": 12345}'POST Position/closeContract
Close an entire position in a given contract. This is equivalent to placing a market order in the opposite direction for the full position size.
URL: POST /api/topstep-sim/Position/closeContract
Authentication: Bearer token required.
Request body
{ "accountId": 12345, "contractId": "CON.F.US.ENQ.H25"}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
contractId | string | Yes | Contract ID of the position to close |
Response
{ "success": true, "errorCode": 0, "errorMessage": null}Example
# Close the entire NQ positionresult = api("Position/closeContract", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID})
if result["success"]: print("Position closed")else: print(f"Close failed: {result['errorMessage']}")curl -X POST https://api.test-max.com/api/topstep-sim/Position/closeContract \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"accountId": 12345, "contractId": "CON.F.US.ENQ.H25"}'Error responses
| Scenario | errorMessage |
|---|---|
| No open position for this contract | "No position found for contract" |
| Invalid account or contract ID | "Account not found" or "Contract not found" |
POST Position/partialCloseContract
Partially close a position by specifying the number of contracts to close. Useful for scaling out of a trade.
URL: POST /api/topstep-sim/Position/partialCloseContract
Authentication: Bearer token required.
Request body
{ "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "size": 1}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
contractId | string | Yes | Contract ID of the position to partially close |
size | number | Yes | Number of contracts to close (must be less than or equal to the current position size) |
Response
{ "success": true, "errorCode": 0, "errorMessage": null}Example
# Scale out: close 1 of 3 contracts at the first targetpositions = get_positions(ACCOUNT_ID)for pos in positions: if pos["size"] > 1 and current_price >= first_target: result = api("Position/partialCloseContract", { "accountId": ACCOUNT_ID, "contractId": pos["contractId"], "size": 1 # Close 1 contract, keep the rest running }) if result["success"]: print(f"[INFO] Scaled out 1 contract at {current_price}")Error responses
| Scenario | errorMessage |
|---|---|
| Size exceeds position | "Size exceeds current position" |
| No open position | "No position found for contract" |
| Size is 0 or negative | "Size must be greater than 0" |
Common patterns
Flatten all positions
Close every open position across all contracts:
result = api("Position/searchOpen", {"accountId": ACCOUNT_ID})
for pos in result.get("positions", []): api("Position/closeContract", { "accountId": ACCOUNT_ID, "contractId": pos["contractId"] })
print("All positions flattened")Scale-out strategy
Exit in stages to lock in profit while letting the remaining position run:
# Entry: buy 3 contractsapi("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 2, "side": 0, "size": 3})
# In your main loop:positions = get_positions(ACCOUNT_ID)if positions: pos = positions[0] pnl_ticks = (bar["c"] - pos["averagePrice"]) / TICK_SIZE
# Take 1/3 off at +10 ticks if pnl_ticks >= 10 and pos["size"] == 3: api("Position/partialCloseContract", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "size": 1 })
# Take another 1/3 off at +20 ticks elif pnl_ticks >= 20 and pos["size"] == 2: api("Position/partialCloseContract", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "size": 1 })
# Let the last contract run with a trailing stopCheck if currently in a position
positions = get_positions(ACCOUNT_ID)is_long = any(p["type"] == 1 for p in positions)is_short = any(p["type"] == 2 for p in positions)is_flat = len(positions) == 0