Synpath
Core Concepts

Orders

Order lifecycle, statuses, and the order types Synpath supports.

Sides and prices

An order names a market, a side and a YES price. Buy takes the YES side and sell takes the NO side, and price is always the YES price, whichever side you are on. Buying NO at 0.30 is written as a sell at 0.70. On Polymarket, where YES and NO are separate tokens, a sell buys the NO token; a sell with reduce_only sells YES tokens you already hold.

Order statuses

Every order carries a status. Four of them are terminal; once an order reaches one it never changes again.

StatusDescription
pendingLive: accepted by Synpath, not yet acknowledged by the venue.
openLive: resting on the venue's book. Read filled and remaining for how much is done.
pending_cancelLive: a cancel has been sent and the venue has not confirmed it.
pending_replaceLive: an edit has been sent and the venue has not confirmed it.
waitingLive: a complex order the engine holds, whose condition has not been met.
triggeredLive: a complex order whose condition fired; its children are on the venue.
closedTerminal: fully filled.
canceledTerminal: ended by a cancel. filled may be non-zero.
rejectedTerminal: refused by the venue or by a risk rule. The reason is on the error.
expiredTerminal: a good-till-date order that reached its date.

An open order that partly fills stays open until it fills completely (closed) or is cancelled (canceled, with the filled part on filled). There is no separate partially-filled status: the numbers say it.

Lifecycle

                    ┌──────────────── venue order ────────────────┐
create_order ──▶ pending ──▶ open ──┬──▶ closed      (fully filled)
                    │               ├──▶ canceled    (cancel_order, filled ≥ 0)
                    │               └──▶ expired     (gtd date reached)
                    └──▶ rejected

                    ┌─────────────── complex order ───────────────┐
engine.submit ──▶ waiting ──▶ triggered ──▶ children: pending ▶ open ▶ closed
                    │                                 (each an ordinary venue order)
                    └──▶ canceled     (engine.cancel pulls every live child)

A complex order is the parent; the limit and market orders it sends when its condition is met are its children. Each child is an ordinary venue order with a parent_id, and cancelling the parent cancels every live child.

Fill fields

Each order carries what its fills add up to:

  • filled and remaining: contracts done and contracts still working.
  • average_price: volume-weighted average YES price of the fills.
  • cost: what the fills came to, in the venue's currency.
  • fee and fee_currency: fees paid so far.
  • last_fill_price and last_fill_amount: the most recent fill.

The fills themselves come from fetch_my_trades, each with its own price, fee, whether you were maker or taker, and a settlement state; on Polymarket a fill is matched until the chain confirms it.

Who holds the order

held_by says where an order lives. A venue order is on the exchange's book and survives your process going away. An engine order is held by the execution engine on your self-hosted server (synpath serve), which must be running; it watches the market and sends venue orders when its condition is met. Orders never pass through Synpath's servers. Every engine decision is written to a journal on disk before it leaves the process, so an engine that crashes restarts holding exactly the orders it had, and never sends one twice. See Create complex order.

Basic order types

Limit

Rests on the book at your price and fills only at that price or better. Every venue holds limits natively, and a limit is what every other type eventually sends.

Example: limit buy 100 at 0.65 fills only at 0.65 or below.

Market

Fills now at the best available price. No prediction market venue has a true market order, so a market order always carries a protection price: price for a venue order, or max_slippage from the touch for one through the engine, which walks the book level by level and stops there. What cannot be filled within that range is reported unfilled rather than bought at any price.

Example: market buy 100 with protection 0.70 takes the asks up to 0.70 and cancels the rest.

Time in force

ValueMeaningExample
gtcGood till cancelled: rests until filled or cancelled.Rest a limit on the book until the market reaches your price.
iocImmediate or cancel: fills what it can now, cancels the remainder.IOC buy 10; if 6 are available, 6 fill and 4 are cancelled.
fokFill or kill: fills completely and immediately, or nothing happens.FOK buy 10; if all 10 cannot fill now, nothing executes.
gtdGood till date: rests until expires_at, unless filled or cancelled first.Limit buy valid until 15:00 UTC on settlement day.
dayGood for the session. No venue has it; the engine rewrites it to gtd at the configured session end.A resting order that should not survive overnight.

Every venue takes gtc, ioc, fok and gtd. post_only rejects a limit that would cross instead of taking. Which venues offer post-only and reduce-only is on the Supported Exchanges page.

Conditional order types

A conditional order is not sent immediately. The engine watches the market and fires it when a price condition is met. Triggering is one way: once fired, the child is out, and a price that comes back does not recall it.

Stop Market and Stop Limit

Fire once when the market reaches stop_price. A buy stop triggers when the price rises to or above the stop; a sell stop triggers when it falls to or below. Stop Market then sends an immediate order at a protection price (the stop plus max_slippage, or your own protection); Stop Limit sends a limit at price.

The market trades at 0.70 and you are long. You want out if it falls to 0.60.
→ Stop Market SELL, stop_price 0.60, max_slippage 0.02
→ Triggers when the best bid ≤ 0.60
→ Sends an immediate sell, no worse than 0.58
What price triggers it. By default a stop watches the touch on the side that would fill it: the best bid for a sell stop, the best ask for a buy stop. Set trigger_source to last to trigger on the last traded price instead, or mid to trigger on the midpoint.

Trailing Stop

A stop whose level follows the market in your favour and never back. A sell trailing stop follows the highest bid seen by trail (in price) or trail_percent; a buy follows the lowest ask. The current level is journaled, so a restart resumes from where the market actually got to.

Long from 0.50, trailing stop SELL with trail 0.05, starting at 0.45
→ Bid rises to 0.62 → stop ratchets to 0.57
→ Bid falls to 0.57 → triggers, sends an immediate sell

Linked order types

One Cancels the Other (OCO)

Two legs where only one should happen: take profit at 0.70 or stop out at 0.40. Each leg is a real order, a limit on the venue or a stop the engine holds. The link is by size, because partial fills are the common case on a thin book: when one leg fills two of five, the other is resized to three, and when one finishes the other is cancelled.

Bracket

An entry, then a take-profit and a stop-loss that protect whatever the entry fills. The exits are sized to the fill as it arrives, so a half-filled entry gets half-sized protection, and they form an OCO between themselves.

Bracket BUY 100: entry limit 0.40, take_profit 0.55, stop_loss 0.30
→ Entry rests at 0.40
→ 60 fill → a sell limit for 60 at 0.55 and a sell stop for 60 at 0.30 go live
→ The remaining 40 fill → both exits grow to 100
→ Either exit finishing cancels the other

Algorithmic order types

Algorithmic orders execute over time as a series of engine-managed children. They live on your engine, not the venue; cancel the parent to cancel everything.

Iceberg

Shows display contracts at price and posts the next slice when one is consumed, until amount is done. reload_delay_s and jitter_s pause between slices so the refill does not look like a clock; follow re-prices each slice to the current touch. Every reload joins the back of the queue at its price, and the parent counts how many reloads it has done.

Buy 1,000 without showing it: Iceberg BUY amount 1000, price 0.45, display 50
→ Slice 1: 50 at 0.45. Filled →
→ Slice 2: 50 at 0.45. Filled → … 20 slices in all

TWAP

Cuts the order into slices spread evenly across window_s seconds, by the clock, not by fill: a slice that misses its turn is carried by the next one, so the order still finishes on time. style is limit (rest at the near touch) or taker (cross at the far touch); finish is complete (take what is left at the end) or stop (abandon it).

Peg

Rests one child at a reference price, the near touch, the far touch or the mid, and re-prices it as the reference moves, within min_price and max_price. Three restraints keep it from burning its queue position: a min_stay_s before any move, a level_cap on how many times it will chase, and the bounds, outside which it waits rather than trades.

Best bid 0.48, best ask 0.50, willing to pay up to 0.49: Peg BUY 200, max_price 0.49
→ Rests 200 at 0.48
→ Bid moves to 0.485 → re-pegs to 0.485
→ Bid moves to 0.495 → above max_price → stays at 0.49 and waits

Smart Taker

Rests nothing on the book. Takes liquidity in clips of clip contracts every interval_s seconds, never worse than limit, until amount is filled or expires_s passes. A clip fires only while the book is inside limit, so a market that moves away is waited out rather than chased.

Want 500, no worse than 0.30: Smart Taker BUY 500, clip 50, interval_s 2, limit 0.30
→ Clip 1: take 50 at 0.29
→ 2 s later, clip 2: asks at 0.31 → skip
→ Asks back to 0.30 → take 50 … until 500 are filled

Comparison

TypeTriggerExecutionPrimary use
MarketImmediateWalks the book to the protection priceNeed a fill now
LimitRests on the bookAt the limit or betterPrice control
Stop MarketPrice moves against youImmediate order with protectionStop loss, breakout entry
Stop LimitPrice moves against youLimit at your priceStop loss with a price floor
Trailing StopPrice reverses by the trailImmediate order with protectionProtect a gain that keeps growing
OCOEither leg fillsThe other leg cancels or resizesTake profit or stop out
BracketEntry fillsExits sized to the fillEnter with protection attached
IcebergImmediate, per sliceSequential limit slicesHide size
TWAPThe clockEven slices across a windowAverage in over time
PegTracks a reference priceResting limit, re-peggedStay at the touch within a bound
Smart TakerLiquidity inside the limitIOC clips, nothing restingTake size without walking the book

Key concepts

Trigger direction

SideStopTrailing stop
buyTriggers when the best ask ≥ stop priceFollows the lowest ask up by the trail
sellTriggers when the best bid ≤ stop priceFollows the highest bid down by the trail

Parent and child

A complex order is a parent that sends children. Cancel the parent and every live child is pulled; read the parent's filled for what the children have done between them. Children appear on the orders stream as ordinary orders with the parent's id on parent_id.

Queue priority

Every venue here treats a price change as cancel and replace, which goes to the back of the queue. Kalshi keeps your place on a pure size decrease, and an edited order reports whether it did on queue_priority_preserved. Icebergs and pegs count their reloads and moves for the same reason.