Trading Engine
SmartRouter, exchange adapters, and order state machine
The trading engine handles order submission, routing, execution, and lifecycle management. It routes orders to the correct exchange, manages state transitions, and supports advanced order types including take-profit/stop-loss, sniper orders, and counter-trade automation.
Order Submission Flow
Client (web/mobile)
│
▼
Trading Controller
POST /api/v1/trading/order
│
▼
┌─────────────────┐
│ SmartRouter │ ← Selects exchange based on market source
│ Service │
└────────┬────────┘
│
┌─────┴─────┐
▼ ▼
┌──────────┐ ┌──────────┐
│Polymarket│ │ Kalshi │
│ Adapter │ │ Adapter │
└────┬─────┘ └────┬─────┘
│ │
└──────┬──────┘
▼
┌────────────────┐
│ OrderState │
│ Machine │
└────────────────┘SmartRouter
The SmartRouterService determines which exchange adapter should handle an order. For markets that exist on a single exchange, the routing is straightforward. For markets available on both Polymarket and Kalshi, the router considers price and liquidity to find the best execution venue.
Exchange Adapters
Exchange-specific adapters live in src/modules/trading/adapters/ and implement a common interface:
| Adapter | Exchange | Details |
|---|---|---|
| Polymarket Adapter | Polymarket | Submits orders via CLOB API using the user's sub-wallet CLOB credentials. Supports market and limit orders. |
| Kalshi Adapter | Kalshi | Submits orders via Trade API v2. Kalshi is custodial, so no wallet signature is needed per-trade. |
Each adapter handles exchange-specific order formats, response parsing, and error mapping.
Order State Machine
The OrderStateMachine enforces valid state transitions for every order:
created ──▶ pending ──▶ partial ──▶ filled
│ │
│ └──▶ filled
│
├──▶ filled
├──▶ cancelled
└──▶ failedTerminal states: filled, cancelled, failed. Once an order reaches a terminal state, no further transitions are allowed.
| State | Description |
|---|---|
created | Order validated and saved to database |
pending | Order submitted to exchange, awaiting confirmation |
partial | Partially filled -- some quantity executed |
filled | Fully filled -- all quantity executed |
cancelled | Cancelled by user or system |
failed | Submission or execution failed |
The Prisma OrderStatus enum includes additional states (SUBMITTING, OPEN, PARTIALLY_FILLED, CANCELLING, REJECTED) that are not used by the OrderStateMachine. The state machine maps its own string states to a subset of the Prisma enum.
Take-Profit and Stop-Loss (TPSL)
Positions can have optional takeProfitPrice and stopLossPrice fields. When set, the system monitors market prices and automatically submits sell orders when the target price is reached:
- Take-Profit -- Sell when
currentPrice >= takeProfitPriceto lock in gains - Stop-Loss -- Sell when
currentPrice <= stopLossPriceto limit losses
TPSL monitoring runs as part of the price-sync cycle.
Sniper Orders
Sniper orders are price-triggered buy orders managed by the SniperMonitorScheduler. Key fields:
| Field | Type | Description |
|---|---|---|
triggerPrice | Decimal(10,6) | Price threshold that triggers execution |
amount | Decimal(18,6) | Order size in USD |
slippage | Decimal(5,4) | Maximum acceptable slippage (default 2%) |
status | SniperStatus | WATCHING, EXECUTING, FILLED, EXPIRED, CANCELLED, FAILED |
expiresAt | DateTime | Expiration timestamp |
repeat | Boolean | Whether to re-arm after filling |
The monitor checks active sniper orders against current market prices and triggers execution when conditions are met.
Counter-Trade Automation
The CounterTradeConfig model allows users to automatically trade inversely against a target wallet address:
| Field | Type | Description |
|---|---|---|
targetWalletAddress | String | Wallet to counter-trade against |
strategy | String | inverse (default) -- take the opposite side |
sizeMultiplier | Decimal(5,2) | Multiplier applied to the target's trade size |
executionDelaySec | Int | Delay before executing the counter-trade |
maxTradeSize | Decimal? | Per-trade size cap |
dailyStopLoss | Decimal? | Daily loss limit before pausing |
categories | String[] | Market categories to counter-trade in |
The CounterTradeResetScheduler resets daily PnL and trade counts at midnight.
Order Fields Reference
Key fields on the Order model:
| Field | Type | Description |
|---|---|---|
side | String | yes or no |
type | String | market or limit |
action | String | buy or sell |
price | Decimal(10,6) | Order price |
quantity | Decimal(18,6) | Requested quantity |
filledQuantity | Decimal(18,6) | Quantity filled so far |
avgFillPrice | Decimal(10,6)? | Average execution price |
fee | Decimal(18,6) | Trading fee |
exchange | String | polymarket or kalshi |
source | String | manual, copy-trade, sniper, counter-trade |
copyTradeSubId | String? | Link to copy trade subscription if applicable |