Skip to content

Sizing & backtest

ML position-sizing for short-premium index option strategies (NIFTY / SENSEX) — naked or hedged.

Our proprietary model scores each option leg's loss-risk at entry and turns it into a position size — it down-sizes the risky legs and keeps the clean ones at your base size, trading every leg. It's a sizing overlay, not a filter — a lower drawdown at the same average exposure, never extra margin. Two horizons (below). Validated out-of-sample on a rolling-quarter walk-forward; see the case study.

The flow: run a backtest once on the /backtest page in the app to validate the edge and get your strategy's weight_norm, then size live with that norm — per leg via /sizing/leg-size for naked strategies, or the whole structure via /sizing/structure for hedged ones.

All endpoints authenticate with X-API-Key: tlyt_… — your TradLyt API key (see Authentication).

Two models (horizon)

Pick the horizon that matches how long you hold:

horizon when model
intraday (default) same-day, square off by close intraday model
positional overnight / multi-day holds positional model (underlying-driven, with a live daily-history lookup)

The sizing runs in derisk mode: it down-sizes the risky legs and never sizes above your base — so you add no extra margin and your average exposure is unchanged. The win is a lower drawdown and a higher risk-adjusted return on the same capital — loss-reduction, not leverage.

Naked vs hedged — use the right endpoint. A naked strategy (straddle / strangle) is sized leg-by-leg with /sizing/leg-size — there's no hedge ratio to protect and the CE/PE tilt is itself edge. A hedged structure (iron-fly / condor / credit spread) must go through /sizing/structure, which scales the whole trade as one unit so the buy:sell hedge ratio is preserved — the protective wings are never stripped.


Live leg sizing (naked)

For each leg you're about to place, get the lots to use. Pass the weight_norm from your backtest.

POST /api/v1/sizing/leg-size

Request body

Field Type Description
underlying enum NIFTY / SENSEX. Index only.
option_type enum CE / PE.
direction enum BUY / SELL. Defaults to SELL.
strike float Strike price.
expiry_date string YYYY-MM-DD.
base_lots integer Your base size in lots. Built for 10+ lots; sizing engages at ≥ 5 (below that it runs flat).
weight_norm float From your backtest's calibrated_weight_norm. Pass this so weights average ≈ 1 for your strategy.
horizon enum intraday (default) or positional — selects the model.
mode enum derisk (default) — the sizing mode.
entry_time string ISO-8601 IST decision moment. Optional; defaults to now.

Example request

{
  "underlying": "SENSEX", "option_type": "CE", "direction": "SELL",
  "strike": 74100, "expiry_date": "2026-06-12",
  "base_lots": 10, "weight_norm": 0.497,
  "horizon": "intraday", "mode": "derisk"
}

Example response

{
  "recommended_lots": 8,
  "risk": 0.63,
  "weight": 0.79,
  "weight_raw": 0.79,
  "norm": 0.497,
  "norm_source": "strategy",
  "mode": "derisk",
  "horizon": "intraday",
  "sizing_active": true,
  "method": "ml",
  "iv": 12.49,
  "delta": 0.534,
  "theta_pct": -12.95
}

Response fields

Field Type Description
recommended_lots integer The answer — the lots to trade for this leg (floored at 1; every leg is kept).
risk float (0-1) The model's loss-risk score for this leg — higher = riskier. null when it ran flat.
weight float The final sizing multiplier on your base lots (after the mode cap). Averages ≈ 1 across a calibrated strategy — allocation, not leverage.
weight_raw float The multiplier before the mode cap. Equals weight unless the cap bit (e.g. clean leg hits the mode ceiling).
norm float The normalization scale actually used — your weight_norm, or the training fallback.
norm_source enum "strategy" (you passed weight_norm — good) / "training" (fallback, may mis-size) / "none" (ran flat).
mode enum The sizing mode applied — derisk.
horizon enum The model used — intraday / positional.
sizing_active bool false when base_lots < 3 or data was unavailable → ran flat (recommended_lots = base_lots).
method enum "ml" (scored on live data) or "rule_based" (flat fallback — model/market data unavailable, never blocks you).
iv float Implied volatility (%) of this contract at entry.
delta float The leg's delta at entry.
theta_pct float Daily theta as a % of premium — the leg's time decay.

Structure sizing (hedged)

Send all legs of a hedged structure in one call. We score each leg, derive a single structure weight from the short (sold) legs' conviction, and apply it to every leg — so the buy:sell ratio you designed is the ratio you trade. Use this for iron-fly, iron-condor, and credit/debit spreads. (Naked structures can be sent here too; they fall back to per-leg sizing.)

POST /api/v1/sizing/structure

Request body

Field Type Description
underlying enum NIFTY / SENSEX. All legs share it.
legs array The structure's legs (see below). Send every leg you intend to trade.
base_lots integer Default base size for legs that don't set their own. Built for 10+ lots; engages at ≥ 5.
weight_norm float The strategy's calibrated_weight_norm from its backtest.
horizon enum intraday (default) or positional.
mode enum derisk (default) — the sizing mode.
entry_time string ISO-8601 IST decision moment. Optional; defaults to now.

Each leg object:

Field Type Description
option_type enum CE / PE.
direction enum BUY / SELL. A structure with any BUY leg is treated as hedged.
strike float Strike price.
expiry_date string YYYY-MM-DD.
base_lots integer Optional — this leg's intended lots; falls back to the request-level base_lots.

Example request — NIFTY iron-fly

{
  "underlying": "NIFTY", "base_lots": 10,
  "horizon": "intraday", "mode": "derisk", "weight_norm": 0.471,
  "legs": [
    {"option_type": "PE", "direction": "BUY",  "strike": 23150, "expiry_date": "2026-06-16"},
    {"option_type": "PE", "direction": "SELL", "strike": 23350, "expiry_date": "2026-06-16"},
    {"option_type": "CE", "direction": "SELL", "strike": 23350, "expiry_date": "2026-06-16"},
    {"option_type": "CE", "direction": "BUY",  "strike": 23550, "expiry_date": "2026-06-16"}
  ]
}

Example response

{
  "legs": [
    {"option_type": "PE", "direction": "BUY",  "strike": 23150, "risk": 0.46, "weight": 0.77, "base_lots": 10, "recommended_lots": 8},
    {"option_type": "PE", "direction": "SELL", "strike": 23350, "risk": 0.40, "weight": 0.77, "base_lots": 10, "recommended_lots": 8},
    {"option_type": "CE", "direction": "SELL", "strike": 23350, "risk": 0.40, "weight": 0.77, "base_lots": 10, "recommended_lots": 8},
    {"option_type": "CE", "direction": "BUY",  "strike": 23550, "risk": 0.50, "weight": 0.77, "base_lots": 10, "recommended_lots": 8}
  ],
  "hedged": true,
  "sizing_mode": "structure-level",
  "structure_weight": 0.77,
  "hedge_ratio": {"designed_sell_buy": 1.0, "sized_sell_buy": 1.0},
  "mode": "derisk",
  "horizon": "intraday",
  "norm": 0.471,
  "norm_source": "strategy",
  "method": "ml"
}

All four legs come back at 8 lots — one structure weight, ratio preserved (designed 1.0 → sized 1.0).

Response fields

Field Type Description
legs[] array One object per leg, in request order: {underlying, option_type, direction, strike, risk, weight, base_lots, recommended_lots}.
hedged bool true if the structure has any BUY (hedge) leg.
sizing_mode enum "structure-level" (hedged → one weight applied to all legs) or "per-leg" (naked → each leg sized on its own).
structure_weight float (hedged) The single weight applied to every leg, taken from the short legs' conviction.
hedge_ratio object {designed_sell_buy, sized_sell_buy} — confirm the ratio is preserved; the two values should match.
norm, norm_source, mode, horizon, method As in /sizing/leg-size.

Send the structure as one call

Sizing each hedge leg separately on /sizing/leg-size drifts the buy:sell ratio (the short-trained model mis-scores the BUY wings, and independent weights diverge) — which can strip your protection. /sizing/structure exists precisely to keep the ratio intact. If any leg can't be priced (expired contract, market closed, token lapsed) the whole structure runs flat — your ratio is never altered.


Backtest

To validate a strategy and get its weight_norm, run a backtest in the app — upload your AlgoTest CSV on the /backtest page and pick the Hold (intraday or positional). You get a flat-vs-sized (De-risk) comparison, the rolling-quarter walk-forward verdict, the calibrated weight_norm to use live, the capital you'll need, and a downloadable per-leg tape. See the case study for worked examples.

The verdict is a rolling-quarter walk-forward: sizing must beat flat in a majority of quarters and not be worse risk-adjusted. If it doesn't, the tool honestly says "run flat."

Keep your weight_norm fresh

weight_norm reflects the volatility regime your backtest covered. As regimes shift, the model's risk distribution moves and the norm drifts — a stale norm can over-size in a vol spike. Re-run your backtest periodically (about quarterly, or sooner after a clear volatility shift) to refresh it. (Automatic drift monitoring is on the way, so you'll be told when.)


Glossary

Every term used by the sizing endpoints and the backtest, in one place.

Term Meaning
risk The model's 0–1 loss-risk score for a leg at entry — higher means a larger adverse excursion is expected. It is not a market-direction call.
weight The multiplier on your base lots: (1 − risk)² / norm, then capped by the mode. ≈ 1 on average for a calibrated strategy.
weight_norm (a.k.a. norm) The per-strategy calibration constant from your backtest. It normalizes weights so they average ≈ 1 — making the model re-distribute size rather than blindly lever up. Pass it on every live call.
norm_source Which norm was used: strategy (you passed weight_norm — correct), training (generic fallback — can systematically over/under-size), none (ran flat).
mode The sizing mode — derisk: down-size the risky legs only, never above your base (no extra margin, lowest drawdown).
horizon Which model scores the leg: intraday (same-day) or positional (multi-day, underlying-driven). Match it to your hold.
sizing_active false ⇒ the call ran flat (base < 3 lots, or data unavailable) and recommended_lots = base_lots.
method ml (scored on live market data) vs rule_based (flat fallback when the model or market data is unavailable — the API never blocks your order).
structure_weight For a hedged structure, the single weight applied to every leg, derived from the short legs' conviction — this is what preserves the hedge ratio.
hedge_ratio designed_sell_buy (the ratio you sent) vs sized_sell_buy (after sizing). They should match.
FLAT (backtest) Your strategy at a constant base size — the baseline we compare against.
ML-SIZED (backtest) The same strategy with our per-leg weights applied.
Rolling-quarter walk-forward (backtest) The verdict method: re-judge sized-vs-flat each calendar quarter out-of-sample. Sizing must win a majority of quarters and not be worse risk-adjusted.
Sharpe (backtest) Return per unit of volatility — higher is a smoother equity curve.
MaxDD (backtest) Maximum peak-to-trough drawdown over the period.
total_return_pct / roi_pct_yr (backtest) Return over the full backtest span, and that figure annualized.

Notes

  • Scope. Sizes short-premium strategies — naked straddles / strangles and hedged structures (iron-fly / condor / spreads), intraday or positional. Hedged trades go through /sizing/structure and are sized as one unit so the hedge ratio is preserved. Tightly-capped structures (e.g. narrow credit spreads) and long / option-buying strategies return "run flat" — don't enable sizing there.
  • De-risk sizing. derisk (default) only down-sizes the risky legs and never sizes above your base → no extra margin and the lowest drawdown, at the same average exposure. Loss-reduction, not leverage.
  • Entry-time only. The model scores a leg's entry features and predicts its excursion from there. It sizes the order you're about to place; it never re-sizes a position you already hold. An adjustment is just a new entry, sized fresh.
  • Internal fields. Raw responses may briefly carry debug-only keys (model name, training label, clip band). They are not part of the contract and may vanish — build only on the fields documented above.
  • Gross of charges. Backtest P&L is gross — we do not net brokerage / STT / slippage. A ~1% round-trip cost roughly halves the edge, so your live net will be lower. (costs_basis: "gross".)
  • Returns are for the whole backtest period. total_return_pct is the return over the full span; roi_pct_yr is that annualized. The result also carries a yearly P&L breakdown and the period.
  • Lot sizes are handled per underlying (NIFTY 65, SENSEX 20 — current).