In the sandbox environment, order execution is deterministic. You exercise specific outcomes (full fill, unfilled cancel, or partial fill) by placing orders with particular field values. We ship a fixed set of scenarios; you cannot add or change them.
Testing execution is enabled on our side for the sandbox environment.
How scenarios are selected
When we route your order for execution, we match it against a predefined list. The first scenario whose conditions all match wins. If nothing matches, you get the default full fill.
Matchers use the same fields you send when placing an order:
- Buy orders: set
cash_amount to the exact string shown below.
- Sell orders: set
quantity to the exact string shown below (and do not set cash_amount).
- Everything else: default full fill.
Trigger values must match exactly. Use "5001", not 5001 or "5001.00". A buy with cash_amount of "5001.00" will not hit the cancel scenario; it falls through to the default full fill. The same applies to sell quantity values (for example "20", not 20 or "20.00").
Scenario reference
| Scenario | How to trigger | Terminal status | Webhooks |
|---|
| Unfilled cancel | Buy, cash_amount: "5001" | cancelled | order.placed → order.executing → order.cancelled |
| Partial fill (40%, buy) | Buy, cash_amount: "5002" | filled | order.placed → order.executing → trade.executed → order.filled |
| Full fill (buy) | Buy, cash_amount: "5003" | filled | order.placed → order.executing → trade.executed → order.filled |
| Full fill (sell) | Sell, quantity of "12" or less, no cash_amount | filled | order.placed → order.executing → trade.executed → order.filled |
| Partial fill (25%, sell) | Sell, quantity: "20", no cash_amount | filled | order.placed → order.executing → trade.executed → order.filled |
| Default full fill | Any other valid order | filled | order.placed → order.executing → trade.executed → order.filled |
Some orders pass through pending before executing; many skip pending entirely. See the order lifecycle for status definitions and the full webhook list.
Partial fills
Partial scenarios are easy to misread. The order ends as filled, not cancelled: we execute part of the order, then cancel what is left. You get trade.executed, then order.filled (not order.cancelled). Use Retrieve order or the trade resource to check traded_quantity and related fields.
| Trigger | Fill % | What to expect |
|---|
Buy, cash_amount: "5002" | 40% | Roughly 40% of the order fills; exact trade quantity and notional depend on the instrument and your order |
Sell, quantity: "20" | 25% | Trade quantity of 5, then order.filled |
The same triggers work for currency pairs. Statuses and webhooks match the table above; trade amounts follow your FX quote rather than the sell example.
Examples
Unfilled cancel
import requests
response = requests.post(
"https://api.engine.usesophic.com/accounts/{account_id}/orders",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
json={
"instrument": "ins_xyz789",
"order_type": "market",
"side": "buy",
"cash_amount": "5001",
},
)
order = response.json()
Expect order.cancelled and a terminal status of cancelled. No trade.executed event.
Partial fill
import requests
response = requests.post(
"https://api.engine.usesophic.com/accounts/{account_id}/orders",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
json={
"instrument": "ins_xyz789",
"order_type": "market",
"side": "buy",
"cash_amount": "5002",
},
)
order = response.json()
Expect trade.executed followed by order.filled. The trade should reflect a partial fill (about 40% of the order); check the response for exact amounts.
Sell partial fill
Use quantity: "20" and omit cash_amount:
import requests
response = requests.post(
"https://api.engine.usesophic.com/accounts/{account_id}/orders",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
json={
"instrument": "ins_xyz789",
"order_type": "market",
"side": "sell",
"quantity": "20",
},
)
order = response.json()
Expect trade.executed with quantity of 5, then order.filled.
Sell full fill
Use a quantity of "12" or less (for example "10") and omit cash_amount:
import requests
response = requests.post(
"https://api.engine.usesophic.com/accounts/{account_id}/orders",
headers={
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json",
},
json={
"instrument": "ins_xyz789",
"order_type": "market",
"side": "sell",
"quantity": "10",
},
)
order = response.json()
Expect trade.executed with quantity of 10, then order.filled.
Tracking results
Subscribe to webhooks for real-time updates, or poll Retrieve order after each placement. Webhook delivery is at-least-once; deduplicate using the event ID or Webhook-Id header.
Sandbox execution does not mirror production routing, venue behavior, or FIX lifecycles. Use it to test your integration against predictable order and trade outcomes, not to validate production execution logic.