Self-trade prevention (STP) stops an incoming order from executing against a resting order owned by the same trading account. The incoming order selects which order Polyester expires when their prices would otherwise match.
STP is scoped to one trading account. A root account and each of its subaccounts are separate STP scopes, so orders from the root account can match orders from its subaccounts, and orders from different subaccounts can match each other. STP does not compare client order IDs, API keys, or strategy names. STP groups are not currently supported.
Modes
| Value | Behavior at a self-trade |
|---|---|
EXPIRE_MAKER | Cancels the resting maker order; the incoming order can continue |
EXPIRE_TAKER | Cancels the remaining incoming order; the resting maker remains |
EXPIRE_BOTH | Cancels the resting maker and the remaining incoming order |
The mode on the incoming order controls the outcome. The resting order's original STP mode does not override it.
self_trade_prevention_mode is omitted or set to SELF_TRADE_PREVENTION_MODE_UNSPECIFIED, Polyester applies EXPIRE_MAKER. Read APIs report the
effective mode as EXPIRE_MAKER.Matching behavior
STP is evaluated when the incoming order reaches a same-account maker in price-time order. Executions against other accounts that occurred earlier remain valid and are not rolled back.
EXPIRE_MAKERremoves the maker's entire remaining quantity, not only the overlapping quantity. The incoming order can then continue through later eligible liquidity.EXPIRE_TAKERpreserves the maker and cancels the incoming order's unfilled remainder.EXPIRE_BOTHremoves the maker's entire remaining quantity and cancels the incoming order's unfilled remainder.
No execution occurs between the two same-account orders.
Set the mode
OrderIntent.self_trade_prevention_mode carries the mode for single and batch order creation.
REST and ConnectRPC ProtoJSON use selfTradePreventionMode:
{
"order": {
"symbol": "BTC-USDT",
"side": "BUY",
"baseQty": "0.01",
"selfTradePreventionMode": "EXPIRE_TAKER",
"limitGtc": {
"price": "50000.00"
}
}
}For protobuf requests, set one of the defined orders.v1.SelfTradePreventionMode values. Generated SDKs expose the same three effective modes
using language-specific names and casing.
EXPIRE_MAKER, sending the intended mode makes order intent,
logs, and client-side reconciliation easier to audit.Post-only and fill-or-kill
Post-only validation takes precedence over STP. A post-only order that would cross the book is
rejected with ERROR_CODE_POST_ONLY_CROSS; STP does not cancel either order.
For fill-or-kill (FOK), same-account makers are not executable liquidity:
- With
EXPIRE_MAKER, Polyester ignores same-account makers when checking whether enough eligible liquidity exists. If the FOK order can fill completely from other accounts, the same-account makers it reaches expire and matching continues. - With
EXPIRE_TAKERorEXPIRE_BOTH, reaching a same-account maker prevents the FOK order from completing.
An FOK order that cannot complete is rejected with ERROR_CODE_FOK_INSUFFICIENT_LIQUIDITY. The
failed FOK attempt does not partially fill or cancel a resting maker.
Trigger child orders
Standalone trigger intents use the same enum in TriggerIntent.self_trade_prevention_mode. The selected mode is applied to each child order when it
reaches the order book. If the field is omitted, child orders default to EXPIRE_MAKER.
Observe the outcome
STP can end an order after its create request was accepted. Use order reads and private realtime
updates to observe the resulting cancellation or remaining working order. Persist the effective selfTradePreventionMode returned with each order so reconciliation reflects the instruction that
Polyester applied.