Syncing trades¶
Push each client's executed trades so TradLyt can build the behavioural history that powers stats, exit analytics and pre-trade.
POST /users/{user_ref}/trades
Two ways to send trades¶
Both land on the same canonical normalised fill schema (documented below) — the only difference is who does the mapping.
- Normalised — start here. Map your fills to the normalised schema yourself and
send
broker_type: "normalized". This works immediately, no setup on our side, and is the right path for your first integration. - Native format — optional, later. Send us a sample tradebook export during
onboarding and we'll build an adapter for your exact format. Once it's registered,
you push your raw export as-is (with your
broker_type) and TradLyt does the mapping — no transformation on your side.
Most brokers begin with path 1 and move to path 2 only if they'd rather not maintain the mapping themselves.
Request body¶
| Field | Type | Description |
|---|---|---|
trades |
array |
Executed legs. Path 1: the normalised schema below. Path 2: your raw native format. |
broker_type |
string |
"normalized" for path 1; the adapter key we register for your tradebook in path 2. |
Normalised fill schema¶
Each fill is one executed leg (not a netted position). Send both legs of a round-trip — the pipeline FIFO-pairs them per symbol, computes charges, and closes the trade.
| Field | Type | Required | Notes |
|---|---|---|---|
symbol |
string |
✅ | Broker trading symbol, e.g. SENSEX2671677500PE. |
trade_type |
enum |
✅ | BUY or SELL. |
quantity |
integer |
✅ | Units, not lots. |
price |
float |
✅ | Fill price. |
execution_time |
datetime |
✅ | ISO-8601 with timezone, or epoch. |
exchange |
string |
NSE / BSE / … |
|
segment |
enum |
EQ or FO. |
|
product |
enum |
MIS / CNC / NRML. |
|
order_id |
string |
Used for dedup when present. | |
trade_id |
string |
Broker fill id — the dedup key. Send it so re-syncs are safe. | |
instrument_category |
enum |
EQ / OPT / FUT. Inferred from symbol when omitted. |
|
option_type |
enum |
CE / PE. |
|
strike_price |
float |
Options only. | |
expiry_date |
string |
YYYY-MM-DD. |
|
underlying |
string |
E.g. NIFTY, SENSEX. |
|
isin |
string |
Equity ISIN, if available. |
{
"broker_type": "normalized",
"trades": [
{
"symbol": "SENSEX2671677500PE",
"trade_type": "SELL",
"quantity": 40,
"price": 429.95,
"execution_time": "2026-07-16T09:45:12+05:30",
"exchange": "BSE",
"segment": "FO",
"product": "MIS",
"order_id": "250716000123456",
"trade_id": "78412345"
}
]
}
Response¶
{
"user_ref": "CLIENT00423",
"fills_received": 2,
"fills_normalized": 2,
"result": {
"status": "completed",
"trades_count": 1,
"positions_closed_count": 1,
"open_positions_count": 0,
"fills_count": 2,
"date_range": { "from": "2026-07-16", "to": "2026-07-16" },
"total_pnl": -430.0,
"total_charges": 41.2,
"errors": [],
"warnings": []
},
"analysis": "queued"
}
analysis tells you what happened to the behavioural analysis:
"queued"— first sync for this client: the full analysis is running asynchronously. PollGET /statsuntilstatus: "ready"."synced"— incremental / live push: trades stored, the new batch retagged, and the aggregate score/pattern cache refreshed;/statsreflects them on the next call (no polling needed)."skipped"— nothing new (all deduped).
First sync & analysis¶
On the first sync, push the client's full history from 2024-04-01 — that's the floor for our market-data-backed Rafiki scoring, so it's the earliest we can analyse fully. (Older trades are accepted and still count toward patterns/score, but can't be Rafiki-scored.)
Storing the fills is synchronous — the response returns as soon as they're stored,
deduped and paired. The heavy behavioural analysis then runs asynchronously (the
same pipeline as TradLyt's own onboarding: retag → 10 pattern detectors → per-trade
Rafiki → personalised thresholds → score). When it kicks off, the response carries
"analysis": "queued".
Poll GET /stats until it returns status: "ready" — until then it
returns status: "analysing" with a stage, so you can show a "building your
insights…" spinner exactly like TradLyt's dashboard does after onboarding.
Chunk a very large first sync
Storing is synchronous, so a multi-year history in a single call makes for a long request. Split the initial backfill into month- or quarter-sized batches; dedup makes overlaps safe, and the analysis re-runs after each batch.
Idempotency & cadence¶
- Sending the same
trade_idtwice is a no-op (deduped) — safe to re-sync. - Partial-day pushes are fine. Sync as often as you like; nightly is the minimum for fresh behavioural baselines.
- Live / intraday: push fills as they execute (or in small batches). After the first
sync these return
analysis: "synced"— fast, no polling;/statsand/pre-tradereflect the new trades on the next call. This is the broker-push equivalent of TradLyt's own per-minute broker sync for direct users.
Order & position monitoring is push-based
Trade sync covers executed fills. For alerts on resting orders / open positions
(a stop about to trigger, a naked position, nearing square-off), push those events to
POST /order-event from your OMS — TradLyt doesn't poll your
order book. (For direct users we poll the broker; for you, you push.)
Payload errors
A fill missing a required key, or with a trade_type that isn't BUY/SELL,
returns 422 bad_trade_payload. The detail.message names the offending fill
index so you can fix the mapping.