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

ParamRequiredNotes
event_idNFilter to one event.
market_idNFilter 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.


Did this page help you?