inNovaAPI
Guides

Limit Orders (GTC / GTD)

Place limit orders that sit on the orderbook until filled or cancelled

Overview

WinNova supports two limit order types:

TypeFull NameBehaviour
GTCGood Till CancelLives on the orderbook until fully filled or you cancel it
GTDGood Till DateSame as GTC but automatically cancelled at a specified expiry time

Limit orders let you specify an exact price rather than accepting the current market price. They are ideal for:

  • Entering at a better price than the current ask
  • Setting up automated take-profit or stop-loss exits
  • Building a position gradually across multiple fills

Required Fields for Limit Orders

FieldTypeDescription
order_typestring"GTC" or "GTD"
limit_pricenumberTarget price between 0.01 and 0.99
token_optionstring"yes" or "no" — which outcome token to trade
expirationintegerUnix timestamp (seconds) — required for GTD only

Place a GTC Buy Order

Buy YES shares at a limit price of $0.50 (you will only fill if the ask drops to 0.50 or lower).

curl -X POST "https://api.winnova.pro/api/ext/v1/orders" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -H "X-API-TIMESTAMP: 1743760000000" \
  -H "X-API-NONCE: abc123" \
  -H "X-API-SIGNATURE: <signature>" \
  -d '{
    "market_id": "550e8400-e29b-41d4-a716-446655440000",
    "outcome": "a",
    "amount": 100,
    "order_type": "GTC",
    "limit_price": 0.50,
    "token_option": "yes"
  }'
order = post("/orders", {
    "market_id": "550e8400-e29b-41d4-a716-446655440000",
    "outcome": "a",
    "amount": 100,
    "order_type": "GTC",
    "limit_price": 0.50,
    "token_option": "yes"
})
order_id = order["data"]["order_id"]
print(f"GTC order placed: {order_id}")
const order = await makeRequest('POST', '/api/ext/v1/orders', {
  market_id: '550e8400-e29b-41d4-a716-446655440000',
  outcome: 'a',
  amount: 100,
  order_type: 'GTC',
  limit_price: 0.50,
  token_option: 'yes'
});

Response:

{
  "success": true,
  "data": {
    "order_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "notification_id": "notif_xyz"
  }
}

The order status will be live once it reaches the orderbook.


Place a GTD Order with Expiry

Same as GTC but the order is automatically cancelled at expiration.

import time

# Expire in 2 hours
expiry = int(time.time()) + 7200

order = post("/orders", {
    "market_id": "550e8400-e29b-41d4-a716-446655440000",
    "outcome": "a",
    "amount": 100,
    "order_type": "GTD",
    "limit_price": 0.48,
    "token_option": "yes",
    "expiration": expiry
})
const expiry = Math.floor(Date.now() / 1000) + 7200; // 2 hours

await makeRequest('POST', '/api/ext/v1/orders', {
  market_id: '550e8400-e29b-41d4-a716-446655440000',
  outcome: 'a',
  amount: 100,
  order_type: 'GTD',
  limit_price: 0.48,
  token_option: 'yes',
  expiration: expiry
});

Monitor a Live Order

A limit order in status live is resting on the orderbook. Fills are partial — size_matched increases as counterparty orders arrive.

result = get(f"/orders/{order_id}")
order  = result["data"]
print(f"Status:      {order['status']}")
print(f"Filled:      {order['size_matched']} / {order['amount']} USDC")
print(f"Avg price:   {order.get('avg_fill_price', 'N/A')}")

Or subscribe via WebSocket for push notifications:

ws.send(JSON.stringify({ action: 'subscribe', topics: [`user:${userId}:orders`] }));

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'data' && msg.data.status === 'live') {
    console.log(`Partially filled: ${msg.data.size_matched} / ${msg.data.amount}`);
  }
};

Cancel a Limit Order

Only orders with status live can be cancelled. FOK orders cannot be cancelled.

curl -X DELETE "https://api.winnova.pro/api/ext/v1/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-API-KEY: your_api_key" \
  -H "X-API-TIMESTAMP: 1743760000000" \
  -H "X-API-NONCE: abc123" \
  -H "X-API-SIGNATURE: <signature>"
def delete(path):
    return requests.delete(f"{BASE_URL}{path}", headers=sign("DELETE", path)).json()

result = delete(f"/orders/{order_id}")
print(result)  # {"success": true}
await makeRequest('DELETE', `/api/ext/v1/orders/${orderId}`);

After cancellation the order transitions to status cancelled and any unfilled amount is returned.


List Your Live Orders

live_orders = get("/orders", {"status": "live"})
for o in live_orders["data"]:
    print(f"{o['id']}  {o['order_type']}  limit={o['limit_price']}  filled={o['size_matched']}/{o['amount']}")

Take-Profit and Stop-Loss

You can attach automatic exit prices when placing any order:

order = post("/orders", {
    "market_id": "550e8400-e29b-41d4-a716-446655440000",
    "outcome": "a",
    "amount": 100,
    "order_type": "GTC",
    "limit_price": 0.50,
    "token_option": "yes",
    "take_profit_price": 0.75,   # auto-sell if price rises to 0.75
    "stop_loss_price": 0.35      # auto-sell if price falls to 0.35
})

Limit Order Lifecycle

placed → pending → signing → submitted → live → matched (fully filled)
                                              ↘ cancelled (manually or GTD expiry)

Partial fills are possible: size_matched increases with each fill until the full amount is consumed.


Tips

  • Price selection: check GET /orderbooks/:asset_id first — your limit price must be at or below the current best ask to be competitive.
  • GTD for time-sensitive bets: use GTD with an expiry before game start to avoid fills after kickoff.
  • Cancel before settlement: if a market closes before your GTC order fills, cancel it manually to avoid unexpected fills on reopened liquidity.

Next Steps