Pre-trade check¶
The core real-time call. Send the order your client is about to place; get back a verdict you render as a warning (or use to gate the confirm button).
This runs the same engine as TradLyt's own extension: rule-based behavioural detectors + the ML bad-trade-risk model (Rafiki) + the Matra position sizer + margin adequacy + a friction level.
POST /pre-trade
Requires the pretrade feature on your account.
Who provides what¶
The engine needs more than the order. Here's the split — most of it is already covered:
| Input | Source |
|---|---|
| Order (instrument, direction, qty, price, …) | You send it (request body below). |
| Broker margin (required + available) | You send it — powers the capital check. See Margin. |
| Today's session state | You send it, or we derive it from synced trades. See session_context. |
| Live market context (India VIX, NIFTY move, underlying LTP) | TradLyt fetches it from a shared market feed. You don't provide it. |
| Behavioural baselines (the client's own history) | TradLyt computes it from the trades you've synced (refreshed daily). |
Request body¶
| Field | Type | Required | Description |
|---|---|---|---|
user_ref |
string |
✅ | The client placing the order. |
instrument |
string |
✅ | Trading symbol. EQ: RELIANCE. OPT: SENSEX2671677500PE. FUT: RELIANCE25MAYFUT. |
direction |
enum |
✅ | BUY or SELL. |
quantity |
number |
✅ | Units, not lots. |
price |
number |
✅ | Intended order price. |
order_type |
enum |
INTRADAY / SWING / CNC / NRML / … |
|
segment |
enum |
EQ / FO. Improves accuracy; inferred from symbol when omitted. |
|
instrument_category |
enum |
EQ / OPT / FUT. Inferred when omitted. |
|
expiry_date |
string |
YYYY-MM-DD (options). |
|
product |
string |
MIS / CNC / NRML. |
|
required_margin |
number |
Margin this order blocks, from your OMS. See Margin. | |
available_margin |
number |
Client's free margin, from your OMS. See Margin. | |
session_context |
object |
Today's live session state. See session_context. |
{
"user_ref": "CLIENT00423",
"instrument": "SENSEX2671677500PE",
"direction": "SELL",
"quantity": 40,
"price": 429.95,
"order_type": "INTRADAY",
"segment": "FO",
"instrument_category": "OPT",
"expiry_date": "2026-07-16",
"product": "MIS",
"required_margin": 58000,
"available_margin": 210000,
"session_context": {
"trades_today": 3,
"session_pnl": -1200,
"orders_in_last_5_min": 1,
"recent_order_instruments": ["SENSEX2671677500PE"]
}
}
session_context¶
Optional, but it changes the verdict. If omitted, we derive trades_today and
session_pnl from the trades you've synced today — which means the state is only as
fresh as your last sync, and orders_in_last_5_min cannot be reconstructed from
settled fills at all.
The difference is not cosmetic. The same order, for the same client, measured against production:
| Request | Verdict |
|---|---|
No session_context |
OK · proceed · nudge · 2 checks |
With {"trades_today": 6, "session_pnl": -42000, "orders_in_last_5_min": 3, "recent_order_instruments": ["RELIANCE","RELIANCE"]} |
CRITICAL · abort · speed_bump · 7 checks, incl. Fomo Entry 62% and Averaging Down 52% |
A client six trades into a ₹42,000 losing day, firing a third order in five minutes, is the exact case this product exists to catch — and it is invisible to us unless you send it. If you send one optional block, send this one.
| Field | Type | Description |
|---|---|---|
trades_today |
integer |
Trades placed today before this one. Drives overtrading detection. |
session_pnl |
float |
Realised P&L for today so far (₹). Negative = loss. Drives revenge-trade flags. |
orders_in_last_5_min |
integer |
Orders in the last 5 minutes. Drives rapid-fire / FOMO detection. |
recent_order_instruments |
array |
Symbols from recent orders, for repeat-entry detection. |
Margin¶
required_margin and available_margin come from your OMS — TradLyt has no broker
session of its own for SDK clients. They drive the capital-adequacy check, which fails
closed:
| You send | Behaviour |
|---|---|
Both required_margin + available_margin |
Full check: ok / warning (>80% utilisation) / critical (order exceeds available). A critical result forces risk_level: CRITICAL and recommendation: abort. |
required_margin only |
A yellow "verify capital manually" warning — never a silent approve. |
| Neither | We fall back to quantity × price as the required estimate; if even that isn't computable, the capital check is simply omitted (behavioural + ML checks still run). |
Response¶
{
"user_ref": "CLIENT00423",
"risk_level": "WARNING",
"recommendation": "pause",
"friction": "hard_warning",
"friction_countdown": 15,
"checks": [
{ "id": "revenge_trading", "label": "Revenge Trading", "status": "warning",
"detail": "You're re-entering 4 min after a ₹1,200 loss." },
{ "id": "capital_check", "label": "Capital adequacy", "status": "ok",
"detail": "Required margin ₹58,000 — 28% of available" }
],
"danger_score": 61,
"danger_level": "high",
"bad_trade_risk": {
"probability": 0.61, "severity": "warning", "method": "ml", "calibrated": true,
"reasons": ["position value", "VIX (16)", "trend strength"]
},
"dominant_risks": ["revenge_trading"],
"hold_time": { "p50": 18 },
"size_suggestion": {
"factor": 0.5, "action": "half", "p_loss": 0.58,
"current_qty": 40, "suggested_qty": 20
},
"required_margin": 58000,
"available_margin": 210000,
"method": "rules+ml",
"personal_edge": { "win_rate": 0.41 }
}
| Field | Type | Description |
|---|---|---|
risk_level |
enum |
OK / WARNING / CRITICAL. Top-level severity. Read this if you read one field. |
recommendation |
enum |
proceed / pause / abort. The suggested action. |
friction |
enum |
UX gate — escalates with risk. nudge = passive banner; speed_bump = a warning the client must actively acknowledge before proceeding; hard_warning = block the confirm button for a cool-off. |
friction_countdown |
integer |
Seconds to hold the confirm button when friction is hard_warning. |
checks[] |
array |
The specific reasons. Each has id, label, status (ok/warning/critical), and a human-readable detail. Render these. |
bad_trade_risk |
object \| null |
Rafiki — probability (0–1) this trade ends badly, plus severity, method (ml / rule_based), and reasons: the top contributing features in plain words, e.g. ["position value", "VIX (16)", "trend strength"]. The reasons are safe to show a client. Note the base rate differs sharply by instrument — equity typically scores 0.05–0.10 and option legs 0.30–0.50, so compare a number against its own segment, never across. |
dominant_risks |
array |
The top firing behaviours (pattern ids). |
hold_time |
object \| null |
Typical hold-time quantiles for similar setups (guidance, not a target). |
size_suggestion |
object \| null |
Matra — a de-risked quantity to offer as a one-tap alternative. suggested_qty is what you render; it is never larger than current_qty. action is full / trim / half / quarter, and factor the raw multiplier. Equity orders only — null for F&O, and null until the client has enough history for a baseline. |
required_margin / available_margin |
number \| null |
Echoed back when the capital check ran. |
method |
enum |
rules+ml (full), or rules+rules_fallback / rules_only when the ML service is unavailable — the verdict is still valid. |
personal_edge |
object \| null |
The client's historical edge on this setup (e.g. win_rate). |
The response carries a few further keys — danger_score, danger_level,
ml_alerts, session_forecast, intent_results, ai_insight,
loss_limit_triggered, and sizer (the raw model output behind
size_suggestion). They are visible for debugging and may change shape
without notice. The table above is the contract; don't couple your UI to
anything outside it.
Rendering guidance¶
WARNING→ show a dismissible warning with thechecks[]reasons, and optionally offer thesize_suggestion.CRITICAL→ block the order or require an explicit override.- Use
friction+friction_countdownfor the confirm-button cool-off.
Decision support, not an order gate
Treat the verdict as a signal — you own the final order flow. And if TradLyt is ever unreachable, fail open: let the order through rather than block your client on our availability.
Active patterns¶
The id values in checks[] and dominant_risks (and in stats) come from
this set:
| ID | Description |
|---|---|
revenge_trading |
Trade placed soon after a loss — matches the client's revenge-trade footprint. |
fomo_entries |
Entry within seconds of a sharp move in the underlying. |
paper_hands |
Client historically exits similar setups before they play out. |
holding_losers |
Client tends to hold losers past their typical hold window. |
averaging_down |
Adding to a losing position. |
ignoring_stop_loss |
For options: letting premium decay past the usual exit without acting. |
inconsistent_sizing |
Position size deviates sharply from the client's bucket average. |
overtrading |
Trades today exceeded the client's personal cap. |
afternoon_collapse |
Post-noon win rate significantly worse than morning. |
expiry_gambling |
Buying weekly options on expiry day (0-DTE long premium). |
Error reference¶
| Status | Cause |
|---|---|
401 |
Missing or invalid subscription key. |
403 |
Account suspended, or the pretrade feature isn't enabled. |
404 |
user_ref not provisioned — call POST /users first. |
429 |
Your account's request ceiling was exceeded — see Authentication. |