Positions
Track your net exposure per market from matched fills, quote and close part or all of it, and poll the close until it settles.
A position is your net matched exposure on one market/strike, built from your fills — not from your resting orders. An order that hasn't matched anything yet doesn't show up here; place and track those with Submit order and GET /v4/mm/get_order_history as usual.
Prices in this guide are on a 0–1 scale, not American odds. Every price field below (best_bid, vwap_price, floor_price, average_price, opposing_price, mark_price) is the implied cost per $1 of payout — the same side's ask and the opposing side's bid always sum to 1. This is different from submit_order's price field, which is American odds. Don't reuse a position price where an order price is expected, or vice versa.
Get your positions
GET /v4/mm/positions
| Param | Required | Notes |
|---|---|---|
event_id | N | Filter to one event. |
market_id | N | Filter to one market. Requires event_id, since market IDs aren't unique across events. |
Each entry nets your two outcomes on a strike into one position: whichever outcome you hold more contracts on is the dominant side (net_outcome_id), and net_size is that side's size minus the other side's. A fully hedged strike (equal size on both outcomes) still appears, with net_size: 0 and no mark_price/mark_value. sides[] always lists every outcome you hold contracts on at that strike, net or not — that's where to look for a hedge.
mark_price/mark_value are a quick valuation (best bid × net size), not a guaranteed execution price — they ignore order book depth. Use quote_close_position for a real, walked-book price before closing.
{
"data": [
{
"sport_event_id": 10079773,
"market_id": 219,
"strike": -3.5,
"net_outcome_id": 4,
"net_size": 30,
"net_cost_basis": 13.5,
"mark_price": 0.44,
"mark_value": 13.2,
"high_risk": true,
"sides": [
{
"outcome_id": 4,
"strike_id": "cffa7ddc4fd50acbe51684e7addabe00",
"size": 50,
"cost_basis": 22.5,
"average_price": 0.45,
"closed_size": 10,
"closable_size": 40,
"liquidation_value": 17.6,
"mark_price": 0.44,
"mark_value": 22.0
},
{
"outcome_id": 7,
"strike_id": "a1e9f23b7c6d40aeb2f5e9c0d8a4b112",
"size": 20,
"cost_basis": 11.0,
"average_price": 0.55,
"closed_size": 0,
"closable_size": 20,
"liquidation_value": 9.2,
"mark_price": 0.46,
"mark_value": 9.2
}
]
}
]
}high_risk is left out entirely (not false) if the sport event or market couldn't be read when the position was computed — treat a missing field as unknown, not as "not high risk."
Per side, closable_size can be less than size: it's forced to 0 once what's left would cost under a cent to close, even if size itself is still positive. liquidation_value is what closing closable_size would currently fetch, walking the order book from the opposing side — it's market-wide (not specific to you), same as mark_price at the side level, which, unlike the position-level mark_price, can include your own resting orders. A quote_close_position call excludes your own resting order from the price it quotes you.
Quote closing a position
POST /v4/mm/quote_close_position
{
"strike_id": "cffa7ddc4fd50acbe51684e7addabe00",
"size": 15
}Walks the opposing side of the order book for the held strike_id, from the closing side's point of view, and prices the full requested size even if the book can't currently fill all of it (fillable_size says how much actually can).
{
"data": {
"requested_size": 15,
"fillable_size": 15,
"best_bid": 0.44,
"vwap_price": 0.435,
"floor_price": 0.42,
"slippage_percent": 1.14,
"gross_proceeds": 6.53,
"cost_basis": 6.75,
"fee_preview": 0,
"net_proceeds": 6.53,
"withdrawable_amount": 6.53,
"locked_amount": 0,
"high_risk": true,
"quote_id": "close_quote_8b2f7e1a4c9d4f0eaa3b6c2d1e5f7a90",
"expires_at": "2026-11-03T18:04:27Z"
}
}A quote is only good until expires_at — 10 seconds after it's issued. Pass its floor_price and quote_id to close_position to execute at that price or better; close_position doesn't require a prior quote, but skipping one means picking floor_price blind.
cost_basis here is just the FIFO-attributed basis for the size being closed, not the position's whole net_cost_basis. fee_preview/fee is not the maker/taker or commission fee described in Trading Fees — closing a position currently always charges a flat percentage of the profit on the close (gross proceeds above cost basis) at a default rate, regardless of your account's regular fee model. A close at a loss or breakeven (as above, where vwap_price is below the 0.45 average entry price) is never charged a fee.
quote_close_multiple_positions batches up to 50 of the same request shape under data[], and prices each independently — one being unpriceable (strike not found, etc.) doesn't fail the others:
{
"data": [
{ "strike_id": "cffa7ddc4fd50acbe51684e7addabe00", "size": 15 },
{ "strike_id": "a1e9f23b7c6d40aeb2f5e9c0d8a4b112", "size": 5 }
]
}{
"data": [
{ "strike_id": "cffa7ddc4fd50acbe51684e7addabe00", "status": 200, "quote": { "...": "..." } },
{ "strike_id": "a1e9f23b7c6d40aeb2f5e9c0d8a4b112", "status": 404, "error": { "error": "not_found", "message": "strike not found" } }
]
}Close a position
POST /v4/mm/close_position
{
"strike_id": "cffa7ddc4fd50acbe51684e7addabe00",
"size": 15,
"floor_price": 0.42,
"ref_id": "close_8900af46-2b89-40fa-a27a-ff65fcb437d2",
"quote_id": "close_quote_8b2f7e1a4c9d4f0eaa3b6c2d1e5f7a90"
}quote_id is optional — include it to execute against that specific quote, omit it to just place the close at your own floor_price. Internally this places a reduce-only order on the opposing side of the strike, filled at floor_price or better; it's asynchronous, so the response only confirms the close was accepted, not that it filled:
{
"data": {
"ref_id": "close_8900af46-2b89-40fa-a27a-ff65fcb437d2",
"status": "pending"
}
}ref_id is yours to set and doubles as an idempotency key: resending the same ref_id returns the existing close instead of starting a second one, so retry on a network error freely rather than generating a new ref_id.
Poll the close status
GET /v4/mm/close_position_status/{ref_id}
Sizes, prices, and fee all read 0 until the close reaches a terminal status (filled, partially_filled, unfilled, or failed) — keep polling while it's pending.
{
"data": {
"ref_id": "close_8900af46-2b89-40fa-a27a-ff65fcb437d2",
"status": "filled",
"requested_size": 15,
"filled_size": 15,
"unfilled_size": 0,
"average_price": 0.435,
"gross_proceeds": 6.53,
"withdrawable_amount": 6.53,
"locked_amount": 0,
"fee": 0,
"high_risk": true,
"created_at": "2026-11-03T18:04:17Z",
"opposing_outcome_id": 7,
"opposing_outcome_name": "Away -3.5",
"opposing_strike_id": "a1e9f23b7c6d40aeb2f5e9c0d8a4b112",
"opposing_price": 0.565
}
}The opposing_* fields describe the order this close actually placed — on the strike's other outcome, at 1 − average_price of what you held.
withdrawable_amount and locked_amount only diverge on a profitable close of a high-risk position: in that case only your cost basis back is withdrawable right away, and the profit above it is locked_amount until the event settles. Anywhere else — a loss, a breakeven close, or a non-high-risk market — the full net_proceeds is withdrawable immediately and locked_amount is 0.
Updated 9 days ago
