Limit Orders (GTC / GTD)
Place limit orders that sit on the orderbook until filled or cancelled
Overview
WinNova supports two limit order types:
| Type | Full Name | Behaviour |
|---|---|---|
| GTC | Good Till Cancel | Lives on the orderbook until fully filled or you cancel it |
| GTD | Good Till Date | Same 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
| Field | Type | Description |
|---|---|---|
order_type | string | "GTC" or "GTD" |
limit_price | number | Target price between 0.01 and 0.99 |
token_option | string | "yes" or "no" — which outcome token to trade |
expiration | integer | Unix 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_idfirst — 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
- Betting Flow — full lifecycle including portfolio tracking and selling
- Orderbooks API — inspect depth before placing
- WebSocket Topics — real-time fill notifications