Synpath
Complex Orders

Create complex order

Create a conditional or algorithmic order: a stop, trailing stop, iceberg, one-cancels-the-other, bracket, TWAP, peg or smart taker.

POST/ordersself-hosted · access token · trade

Self-hosted. Served by synpath serve on your own machine or host, which must be running; the engine and its order types live there. Nothing on this page runs on Synpath's servers.

Complex orders are held by the engine in your self-hosted server (synpath serve), not by the exchange. Send one to the same route as a plain order with type set to the kind you want and params for its settings. The engine watches the market and places ordinary orders on the venue when the condition is met. Cancel the parent and every child is cancelled with it.

Supported types:

  • Stop Market: fires at the stop price, then takes the market
  • Stop Limit: fires at the stop price, then rests a limit
  • Trailing Stop: a stop that follows the market one way
  • Iceberg: shows one slice at a time until the whole size is done
  • One Cancels the Other (OCO): two orders, one cancels the other
  • Bracket: an entry with a take-profit and a stop-loss attached
  • TWAP: slices the order evenly across a time window
  • Peg: rests at the touch and follows the book
  • Smart Taker: takes liquidity in clips, inside a price bound

Pass the type as stop_market, stop_limit, trailing_stop, iceberg, oco, bracket, twap, peg or smart_taker.

The order comes back with held_by: "engine" and status waiting, then triggered once it fires. Its child orders appear in GET /orders with parent_id set.

Request body

NameTypeDescription
market_idstringSynpath id, venue:native.
sidestringbuy (YES) or sell (NO).
amountdecimalTotal contracts across every child.
typestringstop_market, stop_limit, trailing_stop, iceberg, oco, bracket, twap, peg or smart_taker.
pricedecimalThe YES price the children submit at, where the type takes a limit.
stop_pricedecimalThe trigger level, for the stop types.
time_in_forcestring
default gtc
Applied to each child.
expires_atintegerMilliseconds since epoch. The whole order ends then: the engine cancels it and pulls every live child. Refused if already past.
reduce_onlyboolean
default false
Every child may only reduce your position, never open one. Set it on a protective stop: on Polymarket it makes the stop sell the YES you hold rather than buy NO. OCO and bracket legs inherit it unless a leg sets its own.
post_onlyboolean
default false
Each child rests or is refused, never takes. Only for types that rest on the book: iceberg, peg, and twap in its limit style. Refused with 400 on the others; on oco and bracket, set it on the legs.
paramsobjectWhat the type needs; see the tables below.
client_order_id, account, book, trader, tags, notesAs on a plain order.

Stop orders: stop_market, stop_limit

Fires once when the market reaches stop_price. A sell stop watches the best bid, a buy stop the best ask, because that is the price that would fill it. The child is an immediate limit at the protection price (stop_market) or at price (stop_limit).

NameTypeDescription
params.trigger_sourcestring
default touch
touch (the side that would fill), mid or last.
params.protectiondecimalstop_market only: the worst YES price the child may fill at. Defaults to the stop plus max_slippage.
params.max_slippagedecimalHow far past the stop the protection price sits when protection is not given.

Trailing stop: trailing_stop

A stop whose level follows the market one way and never back. Give stop_price as the starting level.

NameTypeDescription
params.traildecimalDistance the stop keeps behind the best price, in price.
params.trail_percentdecimalThe same, as a fraction of the price. One of the two.
params.trigger_source, params.protection, params.max_slippageAs for a stop.

Iceberg: iceberg

Shows one slice of the order at price; when it fills, the next slice is placed at the back of the queue, until amount is done.

NameTypeDescription
params.displaydecimalContracts visible at a time.
params.reload_delay_snumber
default 0
Seconds to wait before the next slice.
params.jitter_snumber
default 0
Random extra delay, so the reload is not a clock.
params.followboolean
default false
Re-price each slice to the current touch instead of the fixed price.

One cancels the other: oco

Two legs; when one fills, the other is cancelled, and a partial fill on one resizes the other.

NameTypeDescription
params.legsarrayExactly two order specifications, each with side, type, price and optionally market_id, amount, stop_price. A leg inherits the parent's market and size where it says nothing.

Bracket: bracket

An entry, then a take-profit and a stop-loss that protect whatever the entry fills, sized to the fill as it arrives.

NameTypeDescription
params.entryobjectThe entry order: type, price.
params.take_profitobjectprice for the profit-taking limit on the opposite side.
params.stop_lossobjectstop_price and optionally price for the protective stop.

TWAP: twap

Slices amount evenly across a window by the clock, patient or aggressive.

NameTypeDescription
params.window_snumberSeconds the whole order is spread over.
params.slicesintegerHow many child orders.
params.stylestring
default limit
limit rests each slice at limit; taker crosses with each slice.
params.limitdecimalThe YES price limit for every slice. Defaults to price.
params.finishstring
default complete
At the end of the window: complete takes what is left, stop gives up on it.

Peg: peg

Rests one child at the touch and re-pegs it as the book moves, with a minimum stay and a cap on how far it chases.

NameTypeDescription
params.referencestring
default near
near pegs to your own side of the book, far to the other side, mid between them.
params.offsetdecimal
default 0
Price added to the reference, in the direction that improves it.
params.min_stay_snumber
default 0
Seconds a child rests before it may be re-pegged.
params.level_capintegerNever chase more than this many levels from where the peg started.
params.min_price, params.max_pricedecimalThe worst YES price the peg may rest at, on a sell and a buy.

Smart taker: smart_taker

Takes liquidity in clips over time, inside a price bound, so a large order does not walk the book at once.

NameTypeDescription
params.clipdecimalContracts per clip. Defaults to the whole amount.
params.interval_snumber
default 1
Seconds between clips.
params.limitdecimalThe worst YES price any clip may fill at.
params.max_slippagedecimalThe same, as a distance from the touch when the order starts.
params.expires_snumberGive up after this many seconds.

Response

The parent order, held_by: "engine", status waiting. Status 201.

from synpath import Client, OrderRequest, OrderType

client = Client(server="http://127.0.0.1:8000")     # your self-hosted server (synpath serve)
parent = await client.create_order(OrderRequest(
    market_id="polymarket:2252244", side="sell", amount=20,
    type=OrderType.TRAILING_STOP, stop_price="0.40",
    params={"trail": "0.03", "trigger_source": "touch"}, book="alpha",
))
print(parent.status)      # waiting
200
{
  "id": "mo-3f9a",
  "client_order_id": "quickstart-1",
  "venue": "kalshi",
  "account": {
    "venue": "kalshi",
    "name": "desk-a",
    "subaccount": null
  },
  "market_id": "kalshi:KXELONMARS-99",
  "side": "sell",
  "type": "trailing_stop",
  "time_in_force": "gtc",
  "status": "waiting",
  "held_by": "engine",
  "price": null,
  "stop_price": "0.40",
  "amount": "20",
  "filled": "0",
  "remaining": "5",
  "average_price": null,
  "cost": null,
  "fee": null,
  "fee_currency": "USD",
  "last_fill_price": null,
  "last_fill_amount": null,
  "post_only": false,
  "reduce_only": false,
  "expires_at": null,
  "created_at": 1789655008000,
  "updated_at": 1789655008000,
  "parent_id": null,
  "queue_priority_preserved": null,
  "book": "alpha",
  "trader": "tester",
  "tags": {},
  "info": {}
}