FNOTrader Algo — Help
A no-code builder for F&O algos. Author multi-leg strategies, backtest them against minute-bar history, and ship the exact same recipe to live trading through your broker. This page documents every feature in the builder, the backtest engine, and the live runtime — with worked examples for each.
Welcome
The execution engine is a standalone Go service that runs configurable multi-leg F&O strategies on your behalf. It pairs three pieces:
- A visual builder (this UI) where you describe a strategy as a recipe — no programming required.
- A backtest engine that replays the recipe against years of minute-bar history (CE/PE OHLC + IV + OI per ATM±10 strike) — NIFTY from May 2021, SENSEX from May 2023, BANKNIFTY from Sep 2024. How far back you can run is set by your plan.
- A live runtime that ships the exact same recipe to your broker — Kite/Dhan/paper — and supervises the trade end-to-end.
Trader's mental model
WHEN some condition is true on these WATCH instruments, FIRE these legs, MONITOR them under these stop/target rules, and EXIT when this other condition is true (or at square-off time).
Builder is laid out the same way: Step 1–2 define your universe, Step 3 defines triggers, Step 4–5 define legs and risk, Step 6 defines how orders go out.
Five-minute quick-start
- Open the builder and pick a Template in Step 1 — e.g. Short Straddle. It seeds the legs for you.
- Skip straight to Step 6 → press ▶ Run Backtest. You'll get a full report in 10–20 seconds covering the last month.
- Tweak the SL/TG in Step 4 → re-run. Compare results in the Backtest History tab.
- Press 💾 Save Strategy. The recipe is now reusable in live trading too.
- From the home screen, attach the saved strategy to a Broker Account and press ▶ Start.
✨ New — build & backtest by just asking your AI
You can now simply ask Claude, ChatGPT or any AI assistant to create a strategy and backtest it using FNOTrader MCP — no Builder clicks needed. Connect once, then talk to it like a colleague:
“Backtest a NIFTY 09:20 short straddle with 30% stop-loss per leg for
2023–2025 and show me the worst days.”
“Try the same on RELIANCE. Then sweep the stop-loss at 20 / 30 / 40%
and tell me which wins on drawdown.”
The AI understands every feature in this manual — all strike-selection modes, stop types, ladders, adjustments, sweeps — and runs on the same engine, data and credits as the Backtest page. Indices and 200+ F&O stocks, plus mutual-fund research.
Get started in two minutes: mcp.fnotrader.com — see what's possible, generate your personal token, and copy-paste the setup for your AI of choice.
Colors & conventions
Badges: BACKTEST works in backtest, LIVE works in live trading, BETA accepted but not fully wired, TODO deferred.
Step 1 · Strategy identity
Three fields define what the strategy is and where it trades.
| Field | What it does |
|---|---|
| Strategy Name | Free-form label — appears in the Strategies tab, save modal, and backtest history. Autofill is disabled so the browser doesn't accidentally fill your login email here. |
| Symbol | The instrument the strategy trades. Searchable dropdown of every F&O index (NIFTY / BANKNIFTY / FINNIFTY / MIDCPNIFTY / SENSEX / BANKEX) plus every F&O stock. The chosen symbol drives ATM-strike resolution, lot-size lookup, and weekly-vs-monthly expiry eligibility. |
| Template | Pre-built recipes you can use as a starting point:
short_straddle, iron_condor,
BTST, STBT,
rolling_straddle, rolling_strangle, or
custom (blank slate). Picking one seeds Steps 4–6. |
Step 2 · Instruments to Watch
The "vocabulary" your conditions can use. Anything you name here is
readable in Step 3 conditions via ALIAS.METRIC.
Scope dropdown
| Scope | What it resolves to | Example alias |
|---|---|---|
| Spot | Underlying spot index/stock | NIFTY.CLOSE |
| Option | One specific strike + CE/PE | OPT_ATM_CE.LTP, OPT_OTM2_PE.IV |
| Rolling ATM Straddle | The platform's published rolling-ATM straddle instrument — the same series the Rolling ATM Straddle chart plots | ROLL_STRDL.VWAP, ROLL_STRDL.STRIKE |
| ATM Straddle (locked strike) | Sum of ATM CE + ATM PE LTPs, at a strike that locks at start or at a chosen time | ATM_STRADDLE.LTP |
| Strangle | Sum of OTMn CE + OTMn PE LTPs | OTM2_STRANGLE.LTP |
| Custom Combiner | Arbitrary linear combination of legs (synthetic) | MY_SYNTH.LTP |
Rolling ATM Straddle
This scope binds a real instrument rather than summing two legs locally, and the difference is not cosmetic. The published series re-picks the ATM continuously as MIN(CE+PE) across the ±5 strikes around spot — the strike market makers re-price first, which is regularly one or two strikes away from the arithmetic nearest — and it carries real traded volume with intraday history.
Because it is a real instrument it seeds from 09:15 like any other, so
VWAP, DAY_HIGH and DAY_LOW on this alias are the
session's values, not "since the strategy started". A locally combined straddle can
give you none of that.
<Alias>.STRIKE is the strike the series is built from right now. It
rolls many times a session (18–47 times on a typical day), so if you enter on a
signal from this series, set the leg's Strike Mode to Rolling ATM — the legs
then sell exactly the strike the signal was computed on. Without that watch the engine
refuses a Rolling ATM leg rather than falling back to the spot-derived ATM, which
would fill a different contract than the one that triggered.
Availability is per underlying, and the expiry dropdown only ever offers expiries that actually have an instrument. NIFTY and SENSEX publish four (current weekly, next weekly, current monthly, next monthly); BANKNIFTY publishes monthly only — it has weekly options but no weekly straddle; the eight optionable MCX commodities publish two each. FINNIFTY, MIDCPNIFTY, BANKEX and stocks publish none — for those, use ATM Straddle (locked strike).
The expiry is always a relative selector, never a date. A saved strategy keeps working after the contract rolls; a hard-coded date would keep pointing at a dead contract, and a dead contract does not error — the feed serves its frozen last price, so the strategy would sit there trading a straddle that had stopped moving. The row shows which contract the selector resolves to today underneath it.
Configuring it — a short straddle on the VWAP break
- Step 2 — add an instrument, Scope = Rolling ATM Straddle, Expiry =
current weekly, Alias =
ROLL_STRDL. (Timeframe only affects bar-based indicators like EMA/ATR on this alias;LTP,VWAP,DAY_HIGH/DAY_LOWandSTRIKEaren't bucketed.) - Step 3A — entry condition, expression:
ROLL_STRDL.LTP < ROLL_STRDL.VWAP - Step 4 — two legs, both SELL, one CE and one PE, Strike Mode Rolling ATM, Distance ATM. For a wider body use OTM1/OTM2 — the distance applies on top of the rolling anchor, not on top of spot.
- Per-leg SL / target and a square-off time as usual.
N-bar
filter unless you specifically want to trade the open.Decide what the exit watches.
ROLL_STRDL keeps rolling after you are
filled — 18–47 times in a session — while the straddle you sold stays pinned to its
strike. An exit condition written against the alias therefore drifts away from the
position it is supposed to be protecting. Prefer per-leg SL/target, or a combined-MTM
exit, for anything that must track what you actually hold.
In a backtest
Fully supported on NIFTY, SENSEX and BANKNIFTY. The replay recomputes the straddle
from archived per-strike bars using the same rule — one value per minute,
min(CE close + PE close) across the ±5 strikes around the forward — so the
alias, its VWAP/DAY_HIGH/DAY_LOW, its
STRIKE, and Rolling ATM legs all behave as they do live. The expiry
selector is re-resolved against each replay day's expiry ladder, so "current weekly"
means that day's weekly, not today's.
It is not a replay of live, and every report using it says so in its Warnings panel. The
strike is re-picked once a minute; live re-picks continuously (~250 ms), so the
two diverge intrabar — which is exactly when a straddle signal tends to fire. And
DAY_HIGH/DAY_LOW here are extremes of per-minute closes,
not intrabar extremes, so they read narrower than live. Treat the result as directionally
indicative, then confirm on a paper Forward Test.
Per-row fields
- Alias — what you'll type in Step-3 expressions. Auto-populates
from Scope but is fully editable. Keep it descriptive
(
OPT_ATM_CEnotfoo). - Timeframe — bucket size for indicator aggregation
(1m / 3m / 5m / 15m / 30m / 1h / 1d). Drives
EMA,RSIetc. on that alias. A 5m TF meansOPT_ATM_CE.EMA(10)reads the 10-bar EMA of 5-min candles, not 10 raw minutes. - Symbol / Expiry / Strike Distance — adapt to the chosen scope.
Add Scope = Option, Alias = OPT_ATM_CE, Option Type = CE, Distance = ATM, TF = 5m. Now in Step 3A you can write:
OPT_ATM_CE.CLOSE > OPT_ATM_CE.EMA(10)
Entry only when ATM CE breaks above its own 10-bar (5m) EMA.
Step 3A · Entry conditions
Multi-row builder. Each row is one boolean clause; rows combine with AND
or OR (your choice). The full expression is evaluated every bar (or
throttled — see Check Frequency) and the
strategy enters on the first true.
Per-row fields
| Field | Purpose |
|---|---|
| Symbol | Step-1 instrument the metric reads from. Defaults to the strategy underlying. |
| Metric | Indicator to evaluate. 40+ choices — see catalog. |
| Metric args | Inline numeric inputs for parameters (e.g. EMA period, MACD fast/slow). All values are user-editable. |
| Operator | How LHS is compared (full list below). |
| RHS | A literal value, another metric on the same instrument, or an instrument — a metric on a DIFFERENT Step-1 instrument (e.g. compare ATM_CE.OI_CHG against ATM_PE.OI_CHG). |
| Change basis | Shown for *_CHG metrics. vs prev bar (default) = this bar minus the previous bar — on a 3-min instrument that IS the 3-minute change. vs day open = running change since today's first data point. vs prev day close (OI only) = change from the previous trading day's closing OI. |
Operator catalog
| Operator | Fires when… |
|---|---|
greater_than / less_than | Plain numeric comparison. |
above / below | LHS above/below a price reference (e.g. above_VWAP). |
crossing | LHS crosses RHS in either direction this bar. |
crossing_up / crossing_down | LHS crosses RHS from below / above this bar. |
moving_up / moving_down | LHS strictly increasing / decreasing N bars in a row. |
inside_channel / outside_channel | LHS is between (or outside) two RHS values. |
entering_channel / exiting_channel | Entered / left the channel this bar. |
new_day_high / new_day_low | LHS prints a fresh intraday extreme. |
range_breakout_up / _down / _either | LHS breaks above/below/either side of the opening-range high/low. Window is user-editable (Start–End times). |
Combine mode
Toggle AND (all rows must fire) or OR (any one). Rows cannot mix AND/OR within one block — for that, save two strategies and cascade them via On-Hit actions.
Check Frequency
How often the expression is evaluated:
tick— every market tick (live) / every minute-bar (backtest).every X-min bar close— only when minute-of-day mod X == 0 (e.g. 5 = 09:20, 09:25, …).
Comparing two instruments — CE vs PE OI crossover
Pick RHS mode instrument to compare one instrument's metric against
another's. The canonical intraday use: two ATM watches (ATM_CE,
ATM_PE, ATM tracking = Auto, Timeframe 3m) and a row
ATM_CE · OI Chg · Crossing Up · instrument · ATM_PE · OI Chg.
Crossings against a moving right-hand side are TRUE crossings — both sides
are read at the previous bar too, so the row is true on exactly the
crossing bar, not while one side merely stays ahead. Pairs without a
previous-bar form (e.g. IV Chg) compile as a plain level check — the
Preview always shows exactly what will run.
Already crossed at the open? Some days the two lines START the session in the crossed state (e.g. CE OI above PE OI from the very first data point) and never cross again — a pure crossing row would sit silent all day. Tick "or fire on day's 1st bar if already crossed" on the crossing row to ALSO enter on the session's first closed bar when the condition already holds there. It evaluates exactly once, on the day's first bar-close check; after that only real crossings fire. Works in live and backtest identically.
On an Auto-tracking ATM watch, a strike change resets OI continuity: the new contract's book has nothing to do with the old one's, so OI-change metrics read 0 until the new strike establishes its own baseline (prices are left untouched).
When conditions evaluate — and when entries fill
With Check Frequency every X-min bar close, the expression is
evaluated ONCE per bar, in the first second after the bar closes — never
mid-bar, never on an intrabar spike, and never against the half-formed
bar the strategy started inside. A condition fires the first time it is
true (a crossing is true for exactly one bar; a level like
LTP > 100 fires once when first satisfied). The entry
then executes immediately — effectively the next bar's open. The
backtest enforces the same discipline: signal on bar X's close → fill at
bar X+1's open (see no-lookahead-bias).
tick mode evaluates in real time, live only at tick granularity
(the backtest's floor is 1-minute bars).
Step 3B · Exit conditions
Same builder as 3A but evaluated after entry. If any exit row fires (when combine = OR) or all exit rows fire (combine = AND), the engine flattens every open leg at next bar's open.
Exit conditions compose with the per-leg SL/TG (Step 4) and Combined SL/TG (Step 5). Whichever triggers first wins — they're not mutually exclusive.
Step 3C · BTST / STBT mode
Buy-Today-Sell-Tomorrow / Sell-Today-Buy-Tomorrow. Two time pickers:
Start Time (today's entry window) and Exit Time (next
trading day's square-off). When both are set, the engine flips
overnight_hold = true and the strategy is held across the
EOD square-off boundary. Entry/Exit conditions 3A/3B remain available
but optional.
Step 3D · Overnight Protection
Carrying short option legs overnight exposes you to gap risk and higher peak margin. Overnight Protection automatically buys a protective hedge against every open short option leg at a set time (typically just before close) and sells it back at a set time the next trading morning — no manual step, and it survives engine restarts (the hedge is rebuilt from the order ledger).
Controls
| Field | What it does |
|---|---|
| Enable | Turns it on. Requires product NRML — MIS is squared off intraday, so there is no overnight to protect.📎 A short straddle on NRML gets hedged and carried; the same straddle on MIS is rejected at launch with "needs NRML". Set Product = NRML in Step 6 first. |
| Strike by | How the hedge strike is picked — always further OTM than the short. Six methods (below).📎 Short 25000 CE, spot 25000. Strike distance = 3 → buys the 25150 CE. Delta ≈ 0.10 → buys whichever strike is ~10-delta today (further out on a low-IV day, nearer on a high-IV day) so the wing cost stays roughly constant. |
| ± strikes | Extra ladder steps applied AFTER the method resolves (e.g. Max-OI + 1). Clamped so the hedge always stays beyond the short. |
| Apply at | IST time the hedges are BOUGHT (default 15:20).📎 15:20 — 10 minutes before the 15:30 close, so the wings are on and confirmed before the session ends and margin is measured. |
| Remove at | IST time they are SOLD next session (default 09:20). Weekends / holidays wait for the next trading session automatically.📎 09:20 — 5 minutes after the 09:15 open, once the opening auction settles and the gap (if any) has printed. A Friday-evening hedge simply waits and sells at Monday 09:20. |
Strike-selection methods
| Method | Picks the OTM strike… |
|---|---|
| Strike distance | N ladder steps further OTM than the short (classic). |
| Premium closest to ₹ | whose premium is nearest your ₹ target — cheap fixed-cost wings. |
| Delta closest to | whose |delta| is nearest a target (e.g. 0.10) — constant risk shape. |
| Max OI strike | carrying the highest open interest — the market's wall. |
| Straddle-width × | a multiple of the ATM straddle premium away — the market's own expected-move yardstick. |
| Underlying % away | nearest spot ± X% of the underlying. |
Step 4 · Strategy Legs
Table of legs the strategy fires. One row per leg.
Main-row columns
| Col | What it does |
|---|---|
| Idle | Leg is deferred — won't fire at entry. Activated only by another leg's execute_other_leg / enter_leg_id action.📎 Mark a BUY-CE wing Idle so it sits dormant; a short CE's SL then wakes it as an emergency hedge. |
| Hedge | Marks a protective BUY leg. Excluded from Move-SL-to-Cost and exits LAST on flatten.📎 On an iron condor, tick Hedge on both BUY wings — on a square-off the shorts close first and the wings last, so you are never momentarily naked. |
| Side | BUY / SELL |
| Fut/Opt | OPT (default) or FUT. FUT hides Type/Strike-Selection cells. |
| Type | CE or PE |
| Strike Selection | How the strike is picked — see below. |
| Strike Value | Adapts to the selection mode.📎 Distance → "OTM2"; Absolute → "24500"; Premium → "₹120"; Delta → "0.30"; Underlying → "+0.5%". The field label changes with the mode you pick. |
| Expiry | Relative (Current / Next / Far Weekly and Current / Next / Far Monthly) or Absolute date. Far = the 3rd upcoming series.📎 Current Weekly always tracks the nearest weekly expiry as weeks roll — a saved strategy stays "this week's expiry" forever without editing. Next and Far systematically trade the later series, in live trading and in backtests alike. |
| Qty (lots) | Multiplied by lot_size at order time.📎 Qty 2 on NIFTY (lot 75) → 150 units sent. On MCX the lot is the real contract size (e.g. CRUDEOILM = 10 barrels) and freeze limits auto-split large orders. |
| ⚙ Settings | Expands the advanced 4-tab panel. |
Strike Selection modes
| Mode | Behaviour | Backtest support |
|---|---|---|
| Distance | ATM / OTM1–15 / ITM1–15 strike steps from ATM. Most common. | ATM±10 — a leg set deeper is replayed at ATM±10, with a warning on the report. Marked (live only) in the dropdown because the replay is then not like-for-like. |
| Absolute | Exact strike number (e.g. 24500). | ATM±10 — a strike outside the archived range is replayed at the nearest archived one. |
| Premium | Engine picks the strike whose LTP is closest to a target ₹ value. | ATM±10 — the search only sees archived strikes, so a deeper target resolves to the nearest one inside the range. |
| Delta | Target |delta| (e.g. 0.30 for ~30-delta). | ATM±10 — as Premium: a low-delta target resolves to the nearest archived strike, which live may not match. |
| Straddle Width | Strike whose premium is N% (or N×) of the ATM straddle premium. | LIVE |
| Rolling ATM | ATM ± distance, anchored on the strike the Rolling ATM Straddle watch is currently built from — MIN(CE+PE) over ±5 strikes, not round(spot/step). Live: needs that watch on the same underlying, and the engine refuses the order without it rather than falling back to the spot ATM. How to configure → | ATM±10 — recomputed per bar in a replay; see the fidelity note under Rolling ATM Straddle. |
| Underlying Price | Signed % offset from spot — e.g. +0.5% on NIFTY=24000 → strike ≈ 24120. | LIVE |
Advanced (⚙) — tabbed panel
Tab 1 · Wait & Trade
Delay this leg's entry until its premium crosses a trigger price. Useful for buying weakness or selling strength after the basket has already fired.
- Wait & Trade Trigger — value + unit
(
ptsabsolute /pts_relrelative to LTP at entry /%). - 📎 Sell a CE, then Wait & Trade Trigger =
pts_rel +10→ the leg only fires once the CE trades ₹10 ABOVE its price when the basket armed — you sell strength (a bounce) instead of chasing a falling premium.
Tab 2 · Stoploss (SL1+)
| SL Type | Meaning |
|---|---|
pct | % of entry premium (e.g. 30 = 30%). |
points | Flat ₹ move from entry (e.g. 15 = ₹15 swing). |
pnl | Per-leg ₹ loss (qty-aware).📎 5000 → exit the leg once it is down ₹5,000 across its full quantity (2 lots of NIFTY = 150 units, so a ₹33/unit adverse move). |
underlying_move / _up / _down | SL trips when spot moves N points or N% in the chosen direction.📎 underlying_move_up 100 on a short CE → exit when NIFTY rises 100 pts from your entry spot, regardless of the option's own premium (a directional stop, not a premium stop). |
delta | SL trips when the leg's |delta| crosses a threshold.📎 0.60 → exit the short CE once it reaches ~0.60 delta (going deep ITM) — a moneyness-based stop that fires the same whether the move was slow or a spike. |
atr | SL = entry ± N × ATR(period). ATR source is a Step-1 watch instrument.📎 1.5 × ATR(14) → a volatility-scaled stop: it sits wider on choppy high-ATR days and tighter on calm days, instead of a fixed % that's too tight in a storm and too loose in a lull. |
SL Wait (sec) — SL breach debounce (next to the tab's Check Frequency): a breached stoploss must STAY breached for this many consecutive seconds before the exit fires; a recovery inside the window resets the timer. Ignores flash-spikes / single-tick wicks. 0 = exit on first breach. The same knob exists at basket level on the Combined Stoploss tab (Step 5).
Per-rung fields: SL Qty (% or lots), On-Hit action, Check Frequency. Add SL2 / SL3 rungs for laddered exits.
On-Hit action catalog
| Action | Effect |
|---|---|
just exit | Close the leg, do nothing else (default). |
keep_leg_running | Don't close — mark SL fired but continue holding.📎 SL1 at 30% with keep_leg_running only triggers a partial (SL Qty 50%) and holds the rest; SL2 at 50% then closes what's left — a two-rung scale-out instead of an all-or-nothing exit. |
re_execute_N_times | Close, then re-fire the same leg up to N times.📎 Short CE with re_execute 2×: SL closes it, the engine re-sells a fresh ATM CE, and can do so twice more — three total attempts to collect premium on a range day. |
close_leg_id | Also close another leg by ID (e.g. spread partner). |
enter_leg_id | Fire a leg marked Idle.📎 Sell a naked CE now; keep a BUY-CE wing marked Idle. If the CE's SL hits, enter_leg_id fires the wing — a protective hedge that only exists once you're in trouble. |
move_sl_to_cost | Move every other leg's SL to its entry price (lock breakeven).📎 On a short strangle, once the CE hits its target the PE's SL jumps to its entry price — the winning side can no longer turn into a loser. |
add_qty_pct | Scale up the leg by N% (martingale-style — use with care).📎 add_qty_pct 100 → when the short PE's SL hits, double its size at the new (higher) premium, betting on mean-reversion. Powerful and dangerous: pair it with a hard combined SL. |
execute_other_leg | Trigger another leg with optional delay (live engine cascade).📎 CE target hit → after a 30s delay, fire a fresh OTM CE sell to keep theta working on the call side while the put runs. |
execute_other_strategy | Launch a separate saved strategy.📎 A directional breakout strategy's target hit launches your "trend-continuation" saved strategy — chaining two independent recipes into one workflow. |
Tab 3 · Target (TGT1+)
Same structure as SL. Target Types include all SL types plus
r_multiple (risk-reward — target distance = N × stop distance).
Tab 4 · Per-leg Trailing
- Trail Trigger — profit ₹ threshold that arms the trail.
- Trail Step — every N₹ further profit, ratchet the stop up.
- Trail Lock — minimum ₹ locked in once trail is armed.
- Check Frequency — tick / xm_close.
Profit Lock & Trail (per leg)
₹-P&L guards on ONE leg, independent of the price trail and of its "Trailing enabled" switch. Profit Locking: when this leg's P&L reaches ₹X, a minimum of ₹Y is locked — if P&L falls back to ₹Y, the leg exits in full. Profit Trailing: every further ₹X of PEAK P&L lifts the locked floor by ₹Y. Evaluated on every tick live (a ₹ guard, like Combined SL); at bar closes in backtest.
The per-leg price trail's Trigger / Step / Lock take a unit — % of entry premium (default) or Pts (absolute premium points). The hints and "What this means" readback speak whichever unit you pick.
Step 4B · Strategy Adjustment Conditions
The engine's most powerful section — modify a LIVE position without stopping the run. Where SL/Target close a leg, adjustment rules re-strike it: roll a threatened short leg further OTM, roll a decayed winner back in, re-center a straddle when spot drifts, or delta-hedge the whole book. Each rule is a card — click + Add rule and pick its type (one rule per type; a duplicate type overrides the earlier card).
A roll closes the leg (fill-confirmed) and re-opens at the target strike off the live option chain; the new leg gets a fresh SL/Target from its own entry. Every action is journaled.
Global guards (apply to every rule)
| Guard | Effect |
|---|---|
| Min CE−PE strike gap | Inversion guard — a roll that would leave the lowest CE strike below the highest PE + gap is aborted whole. Default 100.📎 Gap = 100. A delta-hedge roll wants to push your short 25500 CE down to 25300, but the short PE sits at 25400. Post-roll the CE (25300) would be BELOW the PE (25400) — an inverted, un-manageable book. The gap rule sees 25300 < 25400 + 100 and aborts the whole roll, leaving the position untouched. |
| Auto-action limit | Max rolls/day (ALL rules share this budget) + cooldown seconds between actions. Default 3 / 60s — stops a whipsaw from churning the book.📎 3 / 60s. On a fast trend day your CE rolls at 10:05, 10:40 and 11:15 (3 rolls). At 11:20 a 4th trigger fires — it is ignored and journaled ("budget spent"), so the strategy can't roll itself to death chasing the move. Cooldown also blocks two rolls inside 60 seconds. The count resets next trading morning. |
| Only sold (short) legs | Leg-price / delta rules act on SHORT legs only by default. Uncheck to also act on bought legs.📎 ON (default). On a short strangle with protective BUY wings, a leg-price roll rule ignores the wings and only manages the sold CE/PE. Turn OFF for a LONG straddle where you actually want the rule to roll the bought legs as they move. |
Rule types
1 · Roll / exit a leg at a price level
Per-side premium trigger off each leg's own entry (% / points / absolute ₹). On breach: roll away N strikes, roll in, or exit.
2 · Price-diff band around entry (two-sided)
One band, a different action on each side: LTP ≥ entry + band fires the ▲ action; LTP ≤ entry − band fires the ▼ action.
3 · Roll / exit a leg at a delta level
The delta twin of the price rule — fires on the leg's |delta| off the live chain greeks (a short 0.45δ call is a threatened call regardless of premium). Roll by strikes, exit, or roll AWAY to a target |delta|.
4 · CE/PE divergence — winner & loser actions
Straddle/strangle only. When |CE − PE| crosses a threshold, the winner (decayed, in profit) premium-matches IN and the loser rolls AWAY — in one inversion-guarded action.
5 · Rebalance — roll the cheap side in (premium match)
Straddle/strangle only. When one side's premium reaches N× the other, the CHEAP (decayed) leg rolls IN to the strike worth ≈ the target % of the expensive side — the running side is left alone.
6 · Delta / gamma hedge — re-balance when net delta skews
Watches the whole book's net delta (Σ ±δ·qty / Σqty off the live greeks; SELL negates) — 0 when balanced, drifting toward ±1 as one side runs ITM. Two actions:
- Roll the heavy leg (default) — the side whose |delta| ran up is re-struck AWAY to match the light side's delta.
- Hedge with FUTURES — the classic hedge: BUY/SELL the front-month future against the net delta (rounded to whole lots, optional max-lots cap). It manages itself — re-balances as the book moves, unwinds when net delta falls inside half the trigger, closes with the strategy.
An optional net-gamma trigger fires the same fix earlier, before the delta itself looks bad.
7 · Spot re-center at ATM ± offset
Straddle/strangle only. When the underlying moves ≥ X% from the armed reference, close both legs and re-open at the new ATM ± offset. The reference re-bases after each fire.
Step 5 · Strategy Risk Settings (Combined)
Same shape as the per-leg advanced panel but at the basket level. Four tabs: Wait & Trade, Stoploss, Target, Trailing.
Combined SL/TG fires when the basket-level MTM (sum of all open-leg PnLs) crosses a ₹ or % threshold. On hit, the action choices include:
just exit— flatten all legs.close_current_strategy— explicit version of above.close_and_re_enter_strategy— flatten, then re-fire after a configured delay (capped by NTimes).close_all_losing/close_all_profitable— partial flatten. 📎 On a 4-leg book at +₹2,000 combined, close_all_profitable books the two green legs and lets the two red ones run for a possible recovery.close_all_ce_buys/_ce_sells/_pe_buys/_pe_sells— close by option-type + side. 📎 Spot breaks down hard → close_all_ce_sells removes only your short calls (the safe side isn't earning much anyway) and keeps the short puts + hedges working.move_sl_to_cost_profitable_legs— lock breakeven on the winners.execute_other_leg/execute_other_strategy— cascade.
SL Wait (sec) — the same debounce as the per-leg one, at basket level (next to the group Check Frequency): a breached Combined SL must STAY breached for N consecutive seconds before its action fires; if the basket MTM recovers inside the window the timer resets. Applies to every Combined SL condition (AND groups included). In backtests (1-min bars) waits under 60s are absorbed into the bar; longer waits require the breach to persist across bars.
Profit Lock & Trailing (basket)
Its own tab in Step 5, and a different mechanism from Combined Trailing — the difference is who decides where the floor sits. Combined Trailing gives back from the peak: the floor is always peak − Trail Lock, so you choose how much to hand back, never where the floor lands. Profit Lock & Trailing locks an absolute floor you name: when combined P&L reaches ₹X, a floor of ₹Y is held, lifted by ₹L per further ₹S of peak. Falling back to the floor squares off every leg.
Use Profit Lock when you can state the outcome — "once I'm up ₹20,000 I keep at least ₹18,000". Combined Trailing cannot express that.
Arm one, not both. They are checked on the same tick and the first to fire wins, in the order Combined Trailing → Profit Lock → Combined Target. Arm both and the trail usually closes the position before the lock engages. For the same reason a Combined Target at or below your arm level books the basket before the lock can ever arm — disable the target if you want the lock to run.
Two more things worth knowing. Set lift = step for a constant give-back: with step ₹5,000 and lift ₹2,000 the floor falls further behind as profit grows (₹8,000 behind at a ₹30,000 peak, ₹14,000 behind at ₹40,000). And these are rupee amounts on the whole basket, so scale them with lot count just like the Combined Target — a ₹20,000 lock sized for 10 lots arms far too early on 17.
Both are editable while a strategy is running, from the Forward Test / live position panel. Changing the profit lock can close the position immediately: if P&L has already pulled back below the floor your new numbers imply, you get a warning showing the actual floor, peak and current MTM before it applies.
Step 6 · Execution Settings
| Field | What it does |
|---|---|
| Legs Execution | parallel (all at once), sequential (fire next after prev fills), buy_first (BUYs in parallel, then SELLs after delay).📎 An iron condor with buy_first buys both protective wings first, waits Hold Sell Sec, then sells the ATM legs — so you are never even briefly naked short (and get the margin benefit immediately). |
| Hold Sell Sec | Delay between BUY batch and SELL batch in buy_first mode. Used to lock hedges before opening short exposure.📎 2 → a 2-second gap: long enough for the two wing BUY fills to confirm before the short CE/PE orders go out. |
| If Leg Fails | cancel_all / keep_placed / retry_failed (with Max Retries).📎 4-leg condor, cancel_all: if the far CE wing is rejected (illiquid), the other 3 filled legs are pulled too — you never end up holding a lopsided, unhedged book. |
| Spike Protect % | Skip entry if the basket's spread blows out beyond %.📎 5 → on a news-spike or thin open where the combined bid/ask gaps past 5%, the entry is skipped rather than filled at a bad price. It re-checks on the next bar. |
| Spread Limit % | Reject any leg whose bid/ask spread exceeds %.📎 3 → a far-OTM wing quoting 12 / 13 (≈8% spread) is rejected as illiquid; a liquid ATM leg at 100 / 100.5 (0.5%) passes. |
| Premium Gap % | Reject if the leg's LTP gapped more than % from its prior close.📎 20 → on a gap-open day, a CE that closed ₹80 and opens ₹105 (+31%) is skipped — you avoid selling a premium that has already spiked. |
| Slippage % | Cap acceptable slippage at fill.📎 1 → an order expecting ₹100 accepts fills up to ₹101 (BUY) / down to ₹99 (SELL); a worse fill is flagged as slippage-rejected. This same % is applied to entry AND exit fills in backtests. |
| Run On Days | Mon–Sun checkboxes. Strategy is paused on un-checked days.📎 Tick only Tue + Thu for a NIFTY/expiry-focused seller — the strategy sleeps Mon/Wed/Fri and only fires on your chosen days. |
| DTE Filter | Only enter when days-to-expiry matches the chosen set (discrete: pick 0,1,2…) or range (e.g. 3–7). Basis = weekly or monthly cycle. |
| Max Lots | Hard cap on total lot count across all legs.📎 10 → an exposure-sized basket that computes 14 lots is capped down to 10 — a hard ceiling on position size regardless of what the sizing math returns. |
| Exposure (₹) | Auto-size qty from capital — engine divides this by margin/lot to compute lots.📎 ₹3,00,000 on a straddle needing ~₹1.5L margin/lot → auto-sizes to 2 lots. Change your capital and the qty scales with it — no manual re-entry. |
| Order Type | MKT / LMT / LMT-BUF (limit with N% buffer off LTP).📎 LMT-BUF 0.5% on a BUY at LTP 100 places a limit at 100.5 — fills quickly but caps how far above the touch you pay (protection against a market-order spike fill). |
| Product | NRML (carry) / MIS (intraday).📎 NRML to carry a BTST straddle overnight (and to use Overnight Protection); MIS for an intraday scalp auto-squared-off at 15:25 (23:25 for MCX). |
| Send to Telegram | Mirror lifecycle events to your bot.📎 With it on, entry, each SL/target hit, every 4B roll and the square-off land as messages in your configured Telegram bot — a live audit trail on your phone. |
Step 7 · Summary & Save
A single-page English-prose recap of everything you've configured. Refresh it (↻) any time. Then ▶ Start Strategy (live), 💾 Save Strategy (reusable recipe), or Export JSON / Import JSON (share/version).
Expression language
Conditions internally compile to a small expression language. The builder writes it for you, but you can also type it directly into the Preview box (advanced).
Operators & precedence
| Class | Operators | Notes |
|---|---|---|
| Boolean | AND OR NOT | Case-insensitive; standard precedence (NOT > AND > OR). |
| Comparison | > < >= <= == != | Pair LHS metric to RHS value/metric. |
| Arithmetic | + - * / | Standard math precedence. Use parentheses freely. |
| Grouping | ( ) | Override precedence. |
Symbol resolution
Three styles, all interchangeable:
NIFTY.CLOSE— bare underlying token. Recognised symbols: NIFTY, BANKNIFTY, FINNIFTY, MIDCPNIFTY, SENSEX, BANKEX.ATM_CE.IV,OTM2_PE.OI,ITM3_CE.DELTA— relative option symbols. Strike-offset computed automatically; CE/PE explicit.LEG1.CLOSE,LEG2.IV— refers to leg #1, #2, … of this strategy.ALIAS.METRIC— any alias you defined in Step 2.
Special identifiers
| Token | Value |
|---|---|
TIME | Current bar in HHMM (e.g. TIME >= 0930). |
PREV_CLOSE | Yesterday's last bar close — persists across overnight gap. |
LAST_TICK | Tick-level LTP (not candle-level). Useful for sub-second alerts in live. |
Multi-instrument (Phase 4.6)
You can reference foreign underlyings in the same expression. Common use: trade NIFTY only when the broader market (SENSEX) also agrees.
// NIFTY straddle, only when SENSEX is trending up
TIME >= 0920 AND SENSEX.CLOSE > SENSEX.EMA(10)
Backtest data: only NIFTY and SENSEX have archived spot bars today. BANKNIFTY / FINNIFTY references compile fine but produce a warning + read as 0 (so the gate effectively never fires). Live trading is unaffected — all 6 indices stream.
Arithmetic between metrics
The evaluator supports full arithmetic between metric reads. Strike-wise PCR is the canonical example:
// Strike-wise PCR > 1.5 (puts dominate at ATM = bearish hedging)
ATM_PE.OI / ATM_CE.OI > 1.5
// Straddle premium relative to spot > 1.5%
(ATM_CE.LTP + ATM_PE.LTP) / NIFTY.CLOSE * 100 > 1.5
// Credit-spread net premium > ₹50
LEG1.LTP - LEG2.LTP > 50
Every condition row in Steps 3A/3B has an optional arithmetic
combinator between the primary metric and the operator — a small
dropdown reading — by default. Pick +, −,
×, or ÷ and a second
[Symbol] + [Metric] + [args] block appears, so you
can build composite LHS expressions like
ATM_PE.OI ÷ ATM_CE.OI > 1.5 entirely from dropdowns. The
Preview textarea is still available for power users who want to type
longer compound expressions directly.
Metrics & indicator catalog
Every metric below works on any symbol — spot, option, alias.
Price & structural
| Metric | Returns | Example |
|---|---|---|
LTP | Last traded price | OPT_ATM_CE.LTP > 200 |
OPEN / HIGH / LOW / CLOSE | Current bar's OHLC | NIFTY.CLOSE > NIFTY.OPEN |
DAY_HIGH / DAY_LOW | Intraday extreme | NIFTY.CLOSE >= NIFTY.DAY_HIGH |
PREV_CLOSE | Yesterday's last close | NIFTY.CLOSE > PREV_CLOSE |
VWAP | Volume-weighted avg price (day-anchored) | NIFTY.CLOSE > NIFTY.VWAP |
SMA(N) | Simple moving avg over N bars | NIFTY.CLOSE > NIFTY.SMA(20) |
EMA(N) | Exponential MA | NIFTY.CLOSE > NIFTY.EMA(10) |
WMA(N) | Weighted MA | NIFTY.WMA(14) |
SAR(step, max) | Parabolic SAR (defaults 0.02, 0.2) | NIFTY.CLOSE > NIFTY.SAR(0.02, 0.2) |
Momentum
| Metric | Returns | Example |
|---|---|---|
RSI(N) | Wilder RSI (0–100) | NIFTY.RSI(14) < 30 |
ROC(N) | Rate of change (%) over N bars | NIFTY.ROC(5) > 0.5 |
MACD(fast, slow) | MACD line | NIFTY.MACD(12, 26) > 0 |
MACD_SIGNAL(f, s, sig) | Signal line | NIFTY.MACD(12,26) > NIFTY.MACD_SIGNAL(12,26,9) |
MACD_HIST(...) | Histogram (MACD − signal) | NIFTY.MACD_HIST(12,26,9) > 0 |
STOCH_K(N) / STOCH_D(N, d) | Stochastic %K / %D | NIFTY.STOCH_K(14) > 80 |
ADX(N) | Average Directional Index | NIFTY.ADX(14) > 25 |
CCI(N) | Commodity Channel Index | NIFTY.CCI(20) > 100 |
WILLR(N) | Williams %R (−100..0) | NIFTY.WILLR(14) > -20 |
Volatility & channels
| Metric | Returns | Example |
|---|---|---|
ATR(N) | Avg True Range | NIFTY.ATR(14) > 100 |
BB_UPPER(N, σ) | Bollinger upper band | NIFTY.CLOSE > NIFTY.BB_UPPER(20, 2) |
BB_MIDDLE(N) | BB middle (SMA(N)) | NIFTY.BB_MIDDLE(20) |
BB_LOWER(N, σ) | BB lower band | NIFTY.CLOSE < NIFTY.BB_LOWER(20, 2) |
SUPERTREND(N, mult) | Supertrend value | NIFTY.CLOSE > NIFTY.SUPERTREND(10, 3) |
Volume / OI / CDV
| Metric | Returns | Example |
|---|---|---|
VOLUME | Bar volume | OPT_ATM_CE.VOLUME > 100000 |
OBV | On-Balance Volume | NIFTY.OBV > 0 |
OI | Open interest (options only) | ATM_PE.OI / ATM_CE.OI > 1.5 |
CDV | Cumulative delta volume (signed) | NIFTY.CDV > 0 |
BUILDUP | OI buildup tag (long/short addition/unwinding) | OPT_ATM_CE.BUILDUP == 1 |
OI_CHG | OI change vs the previous bar close (contracts). Bar-anchored: on a 3-min instrument this is the 3-minute change. | ATM_CE.OI_CHG > 8000 |
OI_CHG_PCT | Same, as % of the previous bar's OI | ATM_CE.OI_CHG_PCT > 2 |
OI_CHG_FROM_OPEN | OI change since today's first data point (the day-open basis; every *_CHG metric has a _FROM_OPEN form) | ATM_CE.OI_CHG_FROM_OPEN > 50000 |
OI_CHG_FROM_PREV_CLOSE | OI change from the previous trading day's closing OI (OI only) | ATM_CE.OI_CHG_FROM_PREV_CLOSE > 0 |
PREV_OI / PREV_OI_CHG… | Previous-bar forms — the anchors crossings compile against, so an OI crossing fires on the crossing bar only | (emitted by the builder's Crossing operators) |
Spot instruments have no OI — OI metrics on a spot watch read 0. The condition-row basis picker ("vs prev bar / vs day open / vs prev day close") writes these forms for you; you rarely type them by hand.
Option Greeks
Computed via Black-Scholes (Hull 11e) at risk-free rate 6.5%. Returned per option symbol; backtest computes on-the-fly from (spot, strike, IV, DTE).
| Metric | Returns | Example |
|---|---|---|
IV | Implied volatility (%) | ATM_CE.IV > 25 |
DELTA | Price sensitivity to spot | OPT_ATM_CE.DELTA > 0.4 |
GAMMA | Δ sensitivity to spot | ATM_CE.GAMMA > 0.001 |
THETA | Per-day time decay (₹) | OPT_ATM_CE.THETA < -50 |
VEGA | Per-1%-IV sensitivity (₹) | ATM_CE.VEGA > 20 |
Pivots, CPR, Camarilla
| Metric | Returns |
|---|---|
PIVOT_P, PIVOT_R1..R3, PIVOT_S1..S3 | Standard floor pivots |
CPR_PIVOT, CPR_TC, CPR_BC, CPR_WIDTH | Central Pivot Range bands |
FIB_P, FIB_R1..R3, FIB_S1..S3 | Fibonacci pivots |
CAM_H1..H4, CAM_L1..L4 | Camarilla bands |
TIME & PREV_CLOSE
// Window-only entry
TIME >= 0925 AND TIME < 1100
// Gap-up open
NIFTY.OPEN > PREV_CLOSE * 1.005
Backtest engine
Replays your saved strategy against minute-by-minute history — years of option prices (open/high/low/close), implied volatility and open interest for strikes around the money, plus the index itself. Hit ▶ Run Backtest on any saved strategy; reports are persisted to Backtest History.
External signals — backtest a TradingView strategy (or any signal list)
The Backtest tab has an optional Entry signals row (below the main settings). Upload a CSV of timestamped entry/exit signals and the backtest fires your strategy only when the file says so — your SuperTrend / Pine-script / Python / Excel logic decides when, while everything you built in the Builder decides what: legs, strikes, expiry, SL/Target, trailing, square-off. It works in Single-strategy, Portfolio and Parameter-sweep runs, and costs the same credits as a normal backtest.
Two accepted file formats
- TradingView export, as-is. Put your strategy (not just an indicator) on the chart → Strategy Tester → List of Trades → Export data. Upload the downloaded CSV without editing. Rows TradingView marks as still-open ("Open") are skipped automatically, and a re-exported file's duplicate rows are de-duplicated.
- The template ("Download the template" link) for hand-written or
program-generated signals. Two columns matter —
Date and time(IST, 24h,YYYY-MM-DD HH:MM) andSignal; a third Notes column in the template documents everything and is ignored by the upload.
Signal types
A signal row is a pure entry or exit event — the engine never reads a direction out of it and never changes your strategy's legs because of it. The long/short tag is only a label you can filter on:
| Signal value | Code | Meaning |
|---|---|---|
ENTRY / EXIT | E / X | Untagged entry / exit — counts in every "Use rows" mode. |
LONG ENTRY / LONG EXIT | LE / LX | Entry / exit tagged as the long side of your signal system. |
SHORT ENTRY / SHORT EXIT | SE / SX | Entry / exit tagged as the short side. |
Word order and case don't matter (Entry Long,
exit_short, close long all work). Every entry
needs an explicit exit row — an opposite-side entry does not imply one.
This is the same E/X/LE/LX/SE/SX taxonomy the live Signals page
(webhooks) uses, so a system you backtest from a file can go live on a
webhook unchanged.
The "Use rows" filter
- All signals (default) — every row drives the run.
- Long only — only
LONG ENTRY/LONG EXITrows are used. - Short only — only
SHORT ENTRY/SHORT EXITrows.
Untagged ENTRY/EXIT rows pass every filter. Build
the strategy for the side you want, then pick the matching rows. Example — a
SuperTrend stop-and-reverse export: run a BUY CE strategy with
Long only (holds a call through the long stretches) and a
BUY PE strategy with Short only (holds a put through the
short stretches).
How the gate actually fires
- The signal is a state, not a one-bar event: it is ON from an entry row until the next exit row — riding overnight if the file holds overnight. A signal that fired at 09:05 still admits an entry when your strategy's window opens at 09:20; a position squared off at day-end re-enters next morning while the state is still ON.
- Built with an External Signal condition? If the strategy's entry
(or exit) conditions contain the Builder's External Signal row, the
file drives that exact
SIGNALterm inside your composition — e.g.(TIME ≥ 09:20) AND SIGNAL AND (LTP > VWAP)replays verbatim, so you can gate the signal with time windows and price conditions. - No SIGNAL condition? The gate wraps the whole entry automatically: entry = signal ON AND your entry conditions; exit = signal OFF OR your exit conditions.
- Entries fill at the next bar's open after the combined condition turns true (the engine's standard no-lookahead rule); exits, SL, Target, trailing and square-off all keep working exactly as configured — a stop can close you out while the signal is still ON.
- The engine remains one entry-cycle per day per strategy (plus your configured re-entries), and the report's rules header + warnings strip always state how many signal events were used and which rows.
Backtest vs Live — what matches, what doesn't
| What you build | Backtest | Live / Forward | Plain-English note |
|---|---|---|---|
| Entry & exit conditions (time, price, indicators) | ✓ | ✓ | Same rules, same trigger logic. |
| Per-leg SL / Target / Trailing (ladders) | ✓ | ✓ | Every rung replays; intrabar-SL fills stops off the bar's high/low. |
| Combined (basket) SL / Target / Trailing | ✓ | ✓ | Same basket-MTM maths. |
| Re-entry, spike guard, gap gate, DTE, run-on-days | ✓ | ✓ | All top-of-day gates apply identically. |
| Strike selection (Distance / Premium / Delta / Straddle / Underlying %) | ✓ | ✓ | Dynamic strikes resolve off the same greeks. The archive holds ATM±10, so anything deeper — a low-delta target or a Distance leg past OTM10 — is replayed at the nearest archived strike, and the report says which legs were substituted. |
| External signals (SIGNAL condition) | ✓ | ✓ | Backtest drives it from an uploaded CSV; live from the Signals page's webhook — same E/X/LE/LX/SE/SX taxonomy. |
| BTST / STBT overnight hold | ✓ | ✓ | Position rides the gap; exits next morning at your time. |
| Overnight Protection (auto-hedge) | ✓ | ✓ | All six strike methods — only for overnight (BTST/STBT) strategies. |
| 4B adjustment rules — roll actions | ✓ | ✓ | Rolls close & re-open at the target strike, per bar. |
| Delta hedge — FUTURES action | ✗ | ✓ | The archive has no futures bars, so futures hedging can't be replayed (the delta roll action does). |
| Alert-only rules (auto = off) | ✗ | ✗ | They don't place orders anywhere — nothing to simulate. |
| Costs & slippage | ≈ | ✓ | Backtest uses your flat ₹/leg + slippage%; live is your broker's actual charges. |
| Order fills | ≈ | ✓ | Backtest fills at the bar's open/close; live gets real fills — partial fills, freeze-splits and the odd rejection happen only live. |
| Timing resolution | ≈ | ✓ | Backtest = 1-minute bars; live = ~1-second ticks. Live can act up to a bar sooner, never later. |
| Instruments with history | ≈ | ✓ | NIFTY, SENSEX and BANKNIFTY are backtestable (history from May 2021, May 2023 and Sep 2024 respectively). FINNIFTY, MIDCPNIFTY, stock F&O and MCX have no usable backtest data — use a paper Forward Test. |
| Marketplace, copy trading, credits/billing | ✗ | ✓ | Live-only features — not part of a replay. |
What's loaded
- Spot bars for the primary underlying (NIFTY / SENSEX) + every foreign underlying referenced in expressions (Phase 4.6).
- Option bars (CE/PE OHLC + IV + OI) for the strike offsets the
strategy needs — folded from per-leg Distance + any
ATM_CE/OTM2_PEtokens in the entry/exit expressions. - The day's ATM strike, expiry and days-to-expiry.
On-the-fly Greeks
The archive carries OHLC + IV + OI but not Δ Γ Θ Vega. The engine computes them per-minute from (spot, strike, IV, DTE) via Black-Scholes. Theta ticks down throughout the day as DTE shrinks. Verified by 7 unit tests including ATM sanity, deep ITM/OTM bounds, Γ-peak-at-ATM, vega-peak-at-ATM, garbage-input guards, and put-call parity at 1e-9 precision.
Multi-instrument
Expression tokens like SENSEX.RSI(14) in a NIFTY backtest
transparently load SENSEX spot bars in lock-step. Unsupported foreign
underlyings (BANKNIFTY, FINNIFTY, MIDCPNIFTY, BANKEX) surface a clear
Warnings entry instead of silently zeroing — see
multi-instrument.
No-lookahead-bias
Common backtester bug: signal fires at bar X's close and "fills" at bar X's open. That's physically impossible — the open already happened 60s before the close existed — and inflates results.
This engine enforces a 1-bar defer: indicator signal at bar X → fill at bar X+1's OPEN. The rule applies to entry, exit_condition, per-leg SL/TG, AND combined SL/TG.
Pure-TIME strategies (no bar data read) still fill same-bar — no lookahead since no bar was consulted to decide.
Friction model
Two knobs in the Run-Backtest dialog:
- cost_inr_per_leg — flat ₹ deducted per round-trip (brokerage + STT + exchange + GST + stamp). Typical ₹50–80 for NIFTY.
- slippage_pct — adverse fill at entry and exit. SELL at mid×(1−slip%) on entry, mid×(1+slip%) on exit; BUY direction reverses. You pay both sides.
The report has 3 tabs: Plain, Costs only, +Slippage — so you can see the friction sensitivity instantly.
Check Frequency throttle
Honored independently per surface: entry expression, exit expression, per-leg SL1/SL2/TGT1/TGT2/Trail, and combined SL/TG/Trail. You get three modes:
tick— every minute bar.every_xm— N-min interval from StartHHMM.xm_close— minute-of-day grid (MM mod N == 0).
Use this to model "evaluate SL only on 5-min close" or "Combined SL every minute, per-leg SL every 5 minutes" mixtures.
Strike clamp at ATM±10
The archive only has ATM±10 strike data. Out-of-range legs are clamped to the boundary (with a warning) — never silently skipped. Live trading uses the exact strike you specified.
You'll also see a clamp notification toast in the Builder when:
- You pick a Distance outside ATM±10 (OTM12, ITM15, etc.).
- You pick any dynamic mode (Premium / Delta / Absolute / Straddle / UndPrice) — since the resolved strike is unknown at build time.
Report & History
Every backtest produces:
- Daily P&L list with trade-by-trade detail, ATM, skip reasons.
- Headline stats — total PnL, win rate, day count, best/worst day, max drawdown, avg daily.
- Monthly calendar — hedge-fund-style P&L heat-map.
- Equity curve + running drawdown chart (TradingView Lightweight Charts).
- Monte Carlo & sequence risk — the range of drawdowns your strategy can produce, not just the one history dealt.
- Strategy rules header — exact entry/exit/SL/TG echoed back.
- Warnings panel — strike clamps, unsupported metrics, missing data, and a line describing exactly which advanced features were simulated (overnight carry, protection hedges, adjustment rules).
Every run is persisted to Backtest History — a professional table you can filter by strategy/symbol/result, with one-click 🧪 Forward test and 🚀 Deploy live on each row.
Monte Carlo & sequence risk
Your backtest ran your rules over history once. The wins and losses arrived in one particular order — and that order is an accident of the window you picked. Take the identical set of trades, deal them in a different order, and your worst drawdown changes completely even though the total profit doesn't move at all. Live trading will deal you a different order. The Monte Carlo card on every report re-deals your own P&L series thousands of times so you can see the whole range instead of the single hand history gave you.
The coin-toss intuition. 100 tosses, 55 heads. The total is fixed. But whether the 45 tails arrive nicely spread out or as one brutal run of nine decides whether you'd still be trading at the end. Same edge, same final P&L — completely different experience, and a completely different capital requirement. Drawdown is a property of the order, and your backtest only ever showed you one.
First — what "p95" actually means
Every figure in this card is a percentile: rank all the simulated runs from best to worst, then ask where one of them sits. Read them as frequencies and they stop being jargon.
| Percentile | Say it out loud as… | What it is to you |
|---|---|---|
| p5 | 19 in 20 runs are worse | A lucky run. Don't plan around it. |
| p25 | 3 in 4 runs are worse | A good run. |
| Median (p50) | half the runs are worse | The typical outcome — a coin flip either side. |
| p75 / p90 | 1 in 4 / 1 in 10 runs is worse | Bad patches you should fully expect to live through. |
| p95 | 1 in 20 runs is worse | The sizing number. Common, not a worst case — if twenty traders ran this strategy, one of them would get it. |
| p99 | 1 in 100 runs is worse | The stress case. Survive it; don't budget for comfort. |
| Worst | the single worst of every simulation | Not a limit — just the worst that happened to turn up. Run more simulations and it gets worse. |
The three methods
| Method | What it re-deals | Net P&L | Use it to |
|---|---|---|---|
| Shuffle | The order only — same trades, new sequence. | Never changes | Isolate pure sequence luck. |
| Bootstrap | Which trades happen at all (drawn with replacement). | Varies | See the outcome range for the same edge. |
| Block bootstrap default, blocks of 10 |
Which trades, keeping streaks glued together. | Varies | Get the realistic worst case — this is the one to size on. |
Why Shuffle can never change your P&L. Adding the same numbers in a different order gives the same total — so at the engine's fixed quantity, reordering trades moves the path and nothing else. That's a feature: it isolates drawdown from every other variable. If you want the P&L itself to vary, you have to change which trades occur, and that's what the two bootstrap methods do.
Why Block is the default. Losing days in option selling are not independent — they cluster, because one trending week punishes every single day of it. Plain shuffling scatters those losses apart and makes the strategy look safer than it really is. Block bootstrap keeps them together in runs, which is why it usually reports the deepest and most honest drawdown.
Every number, and what to do about it
| Number | What it actually means | What to do about it |
|---|---|---|
| Observed max DD | The dip your backtest actually produced. | Treat it as one sample, not "the" drawdown. |
| Its percentile rank | Where that dip sits among all the re-deals. | Under the 25th = history was kind to you; expect live to be worse. |
| Median (p50) | The typical dip — half of re-deals are worse. | Your normal bad patch. Expect to meet it. |
| p95 max DD | Beaten only 1 time in 20. | Size your capital on this. The single most useful number in the card. |
| p99 / worst | The stress case. | Confirm you'd survive it — not that it's comfortable. |
| p95 DD ÷ margin | That dip as a share of capital actually at risk. | Past ~30% it's very hard to sit through. Near 100% the account is gone. |
| Risk of ruin | How often the drawdown alone exceeds your whole base. | Anything above zero means cut the quantity, not "accept the risk". |
| Max time underwater | Longest stretch below a previous equity high. | Depth breaks accounts; length breaks conviction. This is what makes traders switch off a strategy that works. |
| Longest losing streak | Consecutive losing days/trades to expect. | Know it before you meet it, so a normal streak doesn't read as a broken strategy. |
| p5 net P&L | 1 re-draw in 20 finishes at or below this. | If negative, the same edge can plausibly lose money over this span. |
| Chance of a losing run | How often the same edge ends net negative. | Above ~20% the edge is thin — trade smaller or lengthen the horizon. |
Reading the two charts
- Equity cone — the bold line is your actual run; the shaded band is the middle 90% of re-deals (p5–p95); the dashed line is the median. If your bold line rides the top of the band, history flattered this strategy — plan for the middle. The faint threads behind are individual re-deals, drawn so the spread feels real rather than theoretical.
- Drawdown histogram — where the drawdown usually lands, with a green marker at what you actually got and an amber marker at the p95. The gap between those two lines is the risk your single Max DD number was hiding.
Settings
Runs entirely in your browser off the report you already have — no re-run, and it costs no backtest credits. It also works on any saved run reopened from Backtest History.
- It can only re-deal what your backtest already contained. If your window holds no crash, no re-deal will invent one — a quiet period resamples into a reassuring, and wrong, answer. Test across a range that includes ugly markets.
- It assumes future trades resemble the tested ones. An overfit strategy resamples into confidently precise nonsense. Monte Carlo measures consistency; it does not validate an edge.
- It does not re-price slippage, liquidity, or a broker margin call landing mid-drawdown. Real ruin arrives earlier than the "risk of ruin" figure suggests.
Parameter sweep — variants, not clones
On the Backtest page, tick Parameter sweep to compare tweaks of ONE strategy without saving a copy per tweak. The composer mirrors the sections your strategy actually configures — each leg's Stoploss row (type / value / then / then) and the enabled Adjustment rules — using the builder's own controls. Change any combination, name it, + Save as variant. Variants persist on the strategy, so the same comparison re-runs later on any date range.
- The run replays the original as the baseline plus every ticked variant; results sort best-P&L first with each variant's changes and its delta vs the original, and each row opens its full report.
- Use this applies the winning variant's settings onto the strategy itself — what won the backtest is what trades, live and in forward test. No cloned strategies.
- Credits: 1 per year of range × runs (original + variants) — quoted on the button before you click.
Live trading · broker adapters
| Adapter | Status | Use case |
|---|---|---|
| stub | READY | No real orders. Lifecycle / smoke tests. |
| paper | READY | Fills at current LTP from quote feed. Realistic dry-run with live data. |
| kite | READY | Zerodha — real orders on the assigned broker account. |
| dhan | READY | Dhan — real orders on the assigned broker account. |
Strategy lifecycle
- Start — registry spawns a supervised goroutine for the strategy.
- Wait for entry — evaluates entry condition every tick (or per check_freq) until firing.
- Resolve strikes — uses the per-leg strike-selection mode (Distance / Premium / Delta / OI-based / etc.).
- Fire legs — atomically, per execution-mode (parallel / sequential / buy-first).
- Monitor — quote feed per leg → per-leg SL/TG → combined SL/TG → trail SL — all gated by their own check_freq.
- Exit — on SL/TG hit, exit-condition fire, sqoff time, or kill-switch.
- Settle — records realised P&L, updates daily-loss counters, marks strategy complete.
Forward Test & live runs · editing a running strategy
A Forward Test is simply a strategy deployed to a Demo / paper broker account — simulated fills at live prices, monitoring only. There is no separate "forward test" object: assign a saved strategy to a Demo account from the Strategies page and it appears under Forward Test. The slot is keyed to the saved strategy, so re-running one you have already tested never consumes another.
Expand any row to reach Positions · Orders · Trades · Logs, and the live override panel. Everything there applies to the running strategy immediately — it does not edit the saved recipe, and it survives a restart.
| Control | What it does |
|---|---|
| Combined SL / Target ₹ override | Replace the basket stop or target mid-run. 0 disables that limit. |
| Combined Trailing | The give-back trail — arm at ₹, then every ₹step raises the floor by ₹lock. Floor always sits below the peak. |
| Profit Lock & Trailing | The absolute-floor guard — the same four fields as the Builder tab of that name. Editable live since 2026-08-12. |
| Pause / Resume | Stop acting on triggers without closing anything. |
| Square-off | Close every open leg at market now. |
| External Close | Book a leg at a price you supply without sending a broker order — for positions you exited outside the platform. |
| Per-leg SL / TGT | Edit an individual leg's stop or target from its row in the positions table. |
Editing Profit Lock on an open position can close it immediately. The floor is derived from the peak MTM the run has seen, not from where P&L is now. If profit has already pulled back below the floor your new numbers imply, the lock is breached the moment it arms. The platform checks this before applying and shows you the actual floor, peak and current MTM — you can still choose to go ahead, and the position will book on the next tick.
Two sizing reminders that bite here. Every ₹ figure is on the whole basket, so it must scale with lot count — a ₹20,000 target or lock built for 10 lots fires far too early on 17. And the account multiplier on the strategy-to-account link multiplies your lots again: a strategy set to 17 lots linked at ×17 trades 289 lots, which will exceed the exchange freeze limit and fail. Keep the size in one place and the multiplier at 1.
Leg execution modes
parallel— all legs fired concurrently (default).sequential— wait for leg N to fill before firing leg N+1.buy_first— all BUY legs in parallel; wait HoldSellSec; then all SELL legs. Locks hedges before opening short exposure.
Fail policies when any leg fails to fill:
cancel_all— square off any filled legs and abort.keep_placed— proceed with the partial basket.retry_failed— retry up to MaxRetries before falling back to cancel_all.
On-Hit actions
Fire on SL or TGT events at the leg level (Step 4 → SL/TGT tabs) or the basket level (Step 5 → Combined SL/TGT). See the Step 4 action catalog for per-leg; basket-level actions are listed in Step 5. All actions can chain and most carry a delay parameter for staged behaviour.
Hedges & Move-SL-to-Cost
- IsHedge flag (checkbox on a leg) — that leg is excluded from Move-SL-to-Cost and is exited last when flatting the basket. Use on BUY legs that protect a SELL.
- Move-SL-to-Cost (Combined SL action) — once profitable legs cross a threshold, move every other (non-hedge) leg's SL to its entry price. Locks breakeven on the open legs.
Risk gate
| Setting | Effect |
|---|---|
| Max Daily Loss | Kill switch — once realised P&L for the day drops below this, no new entries fire (across all strategies). Resets at IST midnight. |
| Max Daily Trades | Cap on entry count per day. |
Webhook entry & exit (OnSignal)
Create a Signal, wire strategies and broker accounts to it, and it
gets its own URL — /api/webhook/sig/{token}, shown behind the
🔗 on the Signals page. POST to it from TradingView, Chartink, Python or
any HTTP source. The token is the auth, so treat the URL as a
password.
One Signal can enter and exit, fan out to several strategies × broker accounts with a per-route quantity multiplier, take the instrument and size from the alert body, ignore duplicate alerts, check the broker is logged in before ordering, and log every step under Signals → Logs.
One URL, and the alert says what it is. Paste the same webhook URL
into every TradingView alert for that strategy; each alert names its type
in signal_type. The Signal's strategy rows then decide what
that alert is allowed to be — a type you never wired is refused and
logged, never quietly turned into something else. Omit
signal_type and every wired row fires, which is how older
integrations keep working unchanged.
| signal_type | Row to wire | What happens |
|---|---|---|
LONG ENTRY | Long Entry (or Entry) | Open a long. A repeat while one is open is a pyramid add when the Signal allows it. |
SHORT ENTRY | Short Entry (or Entry) | Open a short. |
LONG EXIT | Long Exit (or Exit) | Close the long — fully, or partly if the alert carries qty. |
SHORT EXIT | Short Exit (or Exit) | Close the short, same rule. |
ENTRY / EXIT | Entry / Exit | Multi-leg strategies, which have no single side. |
UPDATE | Update risk | Change the stop, target, trailing or profit lock of a position already open. Can never open or close one. |
Short codes (LE, SE, LX,
SX, E, X) work too, as do the words
a TradingView strategy tends to emit — buy,
stoploss, tp, square_off.
Every other field is optional, and anything you leave out keeps the strategy's saved value:
| Sent on | Fields |
|---|---|
| Entry | symbol · fut_opt (FUT/CASH/OPT) ·
expiry · option_type ·
strike_offset · side ·
qty (in lots; qty_units for raw contracts) ·
sl · tgt · trailing ·
legs (multi-leg; replaces the saved legs and their risk settings) |
| Exit | qty — omit it to close the position. A quantity in lots releases
just that much of every open leg and leaves the rest running, so a
straddle stays a straddle; a quantity at or above what is open
closes it anyway. qty_pct does the same as a percentage. |
| Update | sl_inr · target_inr (basket ₹; 0 removes the limit, which is how you go to breakeven) ·
trailing · profit_lock · force ·
leg with sl_price / target_price / trail |
sl and tgt on an entry carry no unit — they
are read in whatever unit the strategy already uses, percent of entry
premium by default or points if the strategy is set that way. A
TradingView alert cannot know which, and making it declare one is how a
30-point stop ends up applied as 30%. Use sl_pct or
sl_points when you do want to be explicit.
Two rules that are easy to trip over: omitted is not zero — leaving
a field out means "don't touch it", while 0 is a real value
that removes a limit — and an empty object ("trailing": {})
reads as omitted, so it cannot wipe a live trail. Numbers may arrive as
strings, since TradingView substitutes its {{…}} placeholders
as text before sending.
A profit lock sent by webhook can close the position. Its floor comes
from the peak MTM the run has seen, not from where P&L is now, so
a lock sent after a pullback may already be breached and would book the
basket on the next tick. The app asks the operator to confirm; an alert has
nobody to ask, so the engine refuses it and logs the floor, peak and
current MTM under Signals → Logs. Add "force": true only if
immediate booking is genuinely what you want.
Test it from a terminal before pointing TradingView at it:
# entry — the type is the only field that always matters
curl -X POST https://algo.fnotrader.com/api/webhook/sig/<signal-token> -H 'Content-Type: application/json' -d '{"signal":{"signal_type":"LONG ENTRY","symbol":"NIFTY","fut_opt":"FUT","qty":2}}'
# book half, leaving the rest running
curl -X POST https://algo.fnotrader.com/api/webhook/sig/<signal-token> -H 'Content-Type: application/json' -d '{"signal":{"signal_type":"LONG EXIT","qty":1}}'
# → {"signal":"NIFTY breakout","started":1,"squared_off":0,"updated":0}
# the counts tell you what the alert actually did
Worked examples — every one of these goes to the same URL:
| Scenario | Row to wire | Alert body |
|---|---|---|
| Go long, exactly as saved | Long Entry | {"signal":{"signal_type":"LONG ENTRY"}} |
| Go long, alert picks instrument, size and risk | Long Entry | {"signal":{"signal_type":"LONG ENTRY","symbol":"NIFTY","fut_opt":"FUT","qty":2,"sl":30,"tgt":50}} |
| Go short | Short Entry | {"signal":{"signal_type":"SHORT ENTRY","qty":2}} |
| Close the position | Long Exit / Short Exit | {"signal":{"signal_type":"LONG EXIT"}} |
| Book part of it (TP1 / TP2 / TP3) | Long Exit | {"signal":{"signal_type":"LONG EXIT","qty":1}} — send again at each level |
| Add to a winner (pyramid) | Long Entry, with Pyramid ticked and Max positions set | the entry body again; the count is how many adds are accepted |
| Reverse | one row typed LX, SE | {"signal":{"signal_type":"SHORT ENTRY","qty":2}} |
| Multi-leg entry | Entry | {"signal":{"signal_type":"ENTRY"}}, or add "legs":[…] to compose the basket |
| Multi-leg exit, whole or part | Exit | {"signal":{"signal_type":"EXIT"}} · {"signal":{"signal_type":"EXIT","qty":1}} |
| Same alert, several brokers | one row per broker, each with a multiplier | unchanged — the multiplier scales the size per account |
| Move to breakeven | Update risk | {"signal":{"signal_type":"UPDATE","sl_inr":0}} |
| Change stop / target / trailing mid-trade | Update risk | {"signal":{"signal_type":"UPDATE","sl_inr":8000}} |
In TradingView the Signal's URL goes in the Webhook URL field and the
body in the Message box — the same URL in every alert for that
strategy. TradingView substitutes its own {{…}} placeholders
before sending, so "qty":"{{strategy.order.contracts}}" works.
Don't pipe {{ticker}} into symbol for derivatives:
a charting ticker like NIFTY1! is not the tradable root.
Staged booking works either way: send an exit alert carrying a
qty at each of your levels, or set SL/TGT ladders on the
legs and let the engine take the position off in stages on its own
triggers, with no alert at all.
Telegram notifications
Tick Send to Telegram in Step 6 to mirror every lifecycle event
to your bot — entry fills, SL/TG hits, leg closes, re-entries,
auto-hedge applied. Configure bot_token + chat_id
in config.yml. Buffered, non-blocking, Markdown-formatted.
Signals · repeat policy (ignore vs pyramid)
A webhook sender can retry or double-fire. Each Signal chooses what a REPEAT same-side entry does while that strategy is already live on that broker: with Pyramid on repeat signals unticked (the default), the repeat is ignored and logged in the signal log — your position cannot be multiplied by a duplicated alert. Ticked, each repeat ADDS another position up to Max positions (a ceiling always applies). A same-side check is side-aware: a Short Entry while a long is open is a reversal, not a repeat, and still fires. Exit signals always close every matching run.
Deployments & mornings
- Only logged-in & enabled brokers can be assigned. The Assign picker offers an account only when it can actually place an order right now; hidden accounts are counted with a note pointing at the Brokers page.
- 08:45 IST pre-open check. On trading days, everyone holding a live deployment gets an email (and WhatsApp, when your 24-hour window is open) listing each deployment and whether its broker is ready — with time to log in before 09:15.
- A blocked launch says why. If the broker is still logged out at launch time, the deployment shows an amber ⚠ with the reason, and the strategy launches by itself the moment the broker is ready.
- No late auto-entries. If the entry time has already passed by the time the broker is ready, the strategy is NOT auto-started for the day — a late entry at a different price is your call, via ▶ Run now.
- Deployment log. The pre-open check, blocked launches and skipped late starts are all recorded per strategy and per portfolio — visible in Logs even on days no run was created.
Strategy recipes
Fifteen worked examples covering the breadth of features. Each is a complete spec you can paste into the builder.
R1 · Short Straddle Beginner
TIME >= 0920SELL CE · ATM · 1 lot · SL 30% · TG 50%
R · Protected BTST straddle Intermediate
SELL CE ATM · 1 lot
R · Self-managing short straddle Advanced
TIME >= 0920 · SELL ATM CE + PERebalance at 2.5× → cheap side to 80%
Leg-price roll-away at +60% (backstop)
R2 · Iron Condor with hedges Beginner
TIME >= 0920SELL CE ATM · SL 30% · TG 50%
BUY PE OTM3 · Hedge ✓
BUY CE OTM3 · Hedge ✓
R3 · EMA-crossover long CE Beginner
TIME >= 0925 AND OPT_ATM_CE.CLOSE > OPT_ATM_CE.EMA(10)R4 · Range Breakout strangle Intermediate
NIFTY.CLOSE range_breakout_either (window 09:15–10:00)R5 · PCR-gated straddle Intermediate
Row 2: ATM_PE · OI · ÷ · ATM_CE · OI · > · 1.5 (use the arithmetic ÷ between the two metrics)
Compiled:
TIME >= 0930 AND (ATM_PE.OI / ATM_CE.OI) > 1.5
R6 · IV-filtered iron fly Intermediate
TIME >= 0920 AND ATM_CE.IV > 18R7 · BTST overnight strangle Intermediate
R8 · Cross-index gate Advanced
TIME >= 0920 AND SENSEX.CLOSE > SENSEX.EMA(10)R9 · Delta-gated CE seller Advanced
TIME >= 0920 AND ATM_CE.DELTA > 0.4 AND ATM_CE.DELTA < 0.6R10 · Multi-level SL ladder Advanced
just exit · check freq tickmove_sl_to_cost on remaining · check freq 5m closeR11 · Combined SL kill-switch + re-enter Advanced
close_and_re_enter_strategy · delay 300s · NTimes 2R12 · 5m TF indicator + 1m execution Advanced
OPT_ATM_CE.CLOSE > OPT_ATM_CE.EMA(10)R13 · ATM CE/PE OI crossover → buy PE Intermediate
ATM_CE and ATM_PE — Option (single strike) · Relative → ATM · ATM tracking Auto · Timeframe 3mATM_CE · OI · Crossing Up · instrument · ATM_PE · OI — true on exactly the bar where CE open interest crosses above PE open interestR14 · OI-Chg crossover — 3-minute flow flip Advanced
ATM_CE · OI Chg · (vs day open) · Crossing Up · instrument · ATM_PE · OI ChgR15 · Multi-timeframe Supertrend BUY / SELL Intermediate
SPOT (NIFTY) at Timeframe 5m and SPOT at 15m — the builder auto-suffixes them to SPOT_5M and SPOT_15M so each is addressable separatelySPOT_5M.CLOSE > SPOT_5M.SUPERTREND(10,3)SPOT_15M.CLOSE > SPOT_15M.SUPERTREND(10,3)< and BUY the PE (or SELL the CE) — same structure, opposite side.R16 · Rolling ATM straddle breaks its VWAP → short straddle Intermediate
ROLL_STRDLROLL_STRDL.LTP < ROLL_STRDL.VWAPa time condition
after 09:30 — see WhyROLL_STRDL; see Whyround(spot/step) on 52% of minutes on 10 Aug — with plain Distance you would routinely sell a different contract than the one that triggered, and nothing in the order book would look wrong. And the exit: ROLL_STRDL keeps rolling after you are filled while your sold straddle stays pinned, so an exit written against the alias slowly stops describing the position it is meant to protect.Known limits
- Backtest strike range — ATM±10 only. Beyond that, legs clamp with a warning. Live trading unaffected.
- Backtest spot underlyings — NIFTY, SENSEX and BANKNIFTY have archived spot bars. FINNIFTY / MIDCPNIFTY / BANKEX references warn and read 0 in backtests. Live trading unaffected.
Glossary
| Term | Meaning |
|---|---|
| ATM | At-the-money. Strike closest to current spot. |
| ATM±N | N strike steps away from ATM (typically 50pt for NIFTY, 100pt for BANKNIFTY). |
| OTM | Out-of-the-money. CE strike > spot, or PE strike < spot. |
| ITM | In-the-money. Opposite of OTM. |
| DTE | Days to expiry. |
| MTM | Mark-to-market — current unrealised P&L. |
| NRML | Carry-forward (overnight) product. |
| MIS | Intraday product — must square off by 15:25 IST. |
| PCR | Put-Call Ratio. OI(PE) / OI(CE) — at a strike or aggregated. |
| IV | Implied Volatility (%). |
| VWAP | Volume-Weighted Average Price (day-anchored). |
| Lookahead bias | Backtester bug where a signal fills at a price that didn't yet exist when the signal computed. |
| Combined SL/TG | Basket-level stop/target on the sum of all open-leg PnLs. |
| Move-SL-to-Cost | Action that moves every other leg's stop to its entry price (lock breakeven). |
| BTST / STBT | Buy-Today-Sell-Tomorrow / Sell-Today-Buy-Tomorrow — overnight hold. |
| Hedge leg | A BUY leg flagged to cap basket loss; excluded from Move-SL-to-Cost; exits last. |
| Idle leg | Leg that doesn't fire at entry — activated only by another leg's execute_other_leg action. |
FAQ
Why are my backtest trades different from yesterday's run?
Most likely you tweaked something in the Builder, or the engine received a lookahead-bias fix / Greeks fix / TF-fix in a recent release. If the recipe is unchanged and pre-dates a fix, results should diverge — the older results were quietly wrong.
My strategy never enters in backtest. Why?
Check the Warnings panel of the report. Common reasons:
- Foreign underlying referenced but unarchived (e.g. BANKNIFTY).
- Out-of-archive strike (Distance > ATM±10 or Premium/Delta/etc. resolving outside).
- Entry condition that depends on an indicator that hasn't warmed up
yet — e.g.
EMA(200)with only ~50 bars in history.
Why is my entry timing one bar later than my signal?
That's the lookahead-bias guard working as intended — see No-lookahead-bias. Pure-TIME entries don't defer; only indicator-driven entries do.
How do I compute strike-wise PCR?
Pick the primary metric (e.g. ATM_PE · OI), then choose
÷ from the arithmetic-combinator dropdown in the same row,
then pick the second metric (ATM_CE · OI). Set the operator
to greater than and the RHS value to 1.5. The
Preview compiles to (ATM_PE.OI / ATM_CE.OI) > 1.5 and the
backtest/live engine evaluates it natively — no special PCR metric needed
because the same UI supports any ratio / sum / difference / product of two
metrics.
What's the difference between "tick" and "xm_close" check freq?
tick evaluates every minute bar. xm_close only
evaluates on bars whose minute-of-day mod X is zero — so 5m_close fires
at 09:20, 09:25, 09:30, … and ignores the bars between. Use xm_close
to model "candle-close confirmation" entry/exit logic.