Skip to content
Back to App

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

FieldTypeDescription
accountIdnumberAccount ID
contractIdstringContract ID for the position
typenumberPosition direction: 1 = Long, 2 = Short
sizenumberNumber of contracts currently held
averagePricenumberAverage 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
}
FieldTypeRequiredDescription
accountIdnumberYesAccount ID

Response

{
"success": true,
"errorCode": 0,
"errorMessage": null,
"positions": [
{
"accountId": 12345,
"contractId": "CON.F.US.ENQ.H25",
"type": 1,
"size": 2,
"averagePrice": 21503.50
}
]
}
FieldTypeDescription
positionsarrayList 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")

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"
}
FieldTypeRequiredDescription
accountIdnumberYesAccount ID
contractIdstringYesContract ID of the position to close

Response

{
"success": true,
"errorCode": 0,
"errorMessage": null
}

Example

# Close the entire NQ position
result = api("Position/closeContract", {
"accountId": ACCOUNT_ID,
"contractId": CONTRACT_ID
})
if result["success"]:
print("Position closed")
else:
print(f"Close failed: {result['errorMessage']}")

Error responses

ScenarioerrorMessage
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
}
FieldTypeRequiredDescription
accountIdnumberYesAccount ID
contractIdstringYesContract ID of the position to partially close
sizenumberYesNumber 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 target
positions = 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

ScenarioerrorMessage
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 contracts
api("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 stop

Check 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