Every number comes from the strategy's receipt (summary.json), written by the backtest
framework (poc/optlab/metrics/stats.py). A blank (--) means the receipt holds null: the number is unknown, not zero.
Amounts are rupees for the sizes in the rules (usually one lot per leg), after brokerage, taxes and exchange charges.
- Net P&L
- Total profit or loss of all closed trades after costs (gross P&L minus brokerage, STT, exchange and SEBI
fees, stamp duty and GST).
- ROI / yr on peak capital
- Net P&L divided by the peak capital, divided by the length of the backtest in years. The peak capital is the
largest amount the strategy had tied up at any moment: the SPAN + exposure margin of all the legs open at the same
time (hedges offset, sampled after every fill), plus the premium paid for long options, which carry no margin.
A simple rate, not compounded:
100 x net / peak capital / years, with years = (end - start + 1 days) /
365.2425. "ROI (period)" is the same without dividing by years. When some sessions have no data (minute files
are missing for some months), the strategy page also shows the rate over the years that have data
(roi_annual_data_pct: years x the share of sessions with data). Receipts written before the capital fields existed
used the peak margin, summed per day over the trades open that day.
- Capital basis, peak margin and peak capital
- margin: the strategy only sells or hedges, and its capital is its SPAN + exposure margin (peak capital =
peak margin). premium paid: it only buys options, and its capital is the premium paid plus the charges of
opening. mixed: both at different times or together (for example a debit spread). The averages shown are
weighted by how long each position was open. A trade's own ROI uses the capital of that trade at entry.
- ROI minus repo
- ROI per year minus the average RBI repo rate over the same dates. The repo rate is roughly what the same money
earns at no risk, so a strategy is only worth the risk when this is clearly above zero.
- Sharpe (annual, over repo)
- Return per unit of risk. Each week's net P&L is divided by the peak capital and the week's repo rate is
subtracted; the Sharpe ratio is the mean of those weekly numbers divided by their standard deviation, times
sqrt(52):
mean / sd x sqrt(52). Weeks without a trade count as 0; weeks with a day the data could not
answer are left out. Each receipt states its exact method (shown on the strategy page as "Sharpe method").
Above 1 is good; below 0 means the strategy did worse than the repo rate.
- Sharpe (annual, rf 0)
- The same weekly returns and weeks without subtracting the repo rate (a risk-free rate of 0), the figure most
published Sharpe ratios quote. It is higher than the Sharpe over repo by about the repo rate divided by the
annual volatility of the weekly returns, so a strategy with little capital at work shows a large gap.
- t-stat
- How sure we can be that the average trade is really positive and not luck:
mean(net per trade) / (sd / sqrt(n)) with n trades. Around 2 or more (or -2 or less) means the average is
unlikely to be noise (about 95% confidence); near 0 means the sample does not show an edge either way.
- Cycles (t-stat, P(loss) and Sharpe per cycle)
- A strategy can tag the trades that belong together with a cycle id, for example the legs of one position opened
as separate trades, a hedge and the trade it protects, or everything done for one expiry. The trades of a cycle
are then added up and the t-stat, P(loss) and a Sharpe ratio are also computed over cycles (shown as "t-stat per
cycle" with the number of cycles), because separate legs of one position are not independent trades and distort
the per-trade numbers. The cycle Sharpe is mean / sd of (cycle net / peak capital) x sqrt(cycles per year), with no
risk-free rate. The verdict still uses the per-trade numbers.
- P(loss)
- The chance that the whole backtest would have lost money, estimated by bootstrap: the list of trade P&Ls is
resampled 10,000 times (in blocks of consecutive trades, so losing streaks stay together) and P(loss) is the share
of resamples whose total is below zero. The 5th and 95th percentiles of those totals are shown as the bootstrap
range.
- Max drawdown
- The largest fall of cumulative net P&L from its previous high (starting from 0), in rupees and as a percent of
the peak margin. The red area under the P&L chart is the drawdown at each date.
- Win rate, best and worst trade, worst intraday MTM
- Win rate is the share of trades with net P&L above zero. Worst trade is the largest loss of one closed trade.
Worst intraday MTM is the lowest mark-to-market of any single open trade while it was open (can be worse than the
closing loss).
- Coverage and unknown days
- Eligible days are the days the rules could trade (for example, expiry days). Traded days had a trade.
Unknown days are eligible days the data could not answer: a missing data file, no quote in the entry window,
or a rule that could not be evaluated. Unknown days are left out of every statistic, not counted as zero-P&L
days; their reasons are listed on each strategy page. Many unknown days make every number above less
reliable.
- Verdict
- Set by the framework from the numbers above (poc/optlab/receipts.py):
insufficient data if fewer than 30 trades; promising if t-stat ≥ 2 and ROI per year is above the
repo rate; weak if t-stat ≥ 1 and net P&L is positive; losing if net P&L is negative and
t-stat ≤ -1; otherwise no edge.
- Status
- running: the backtest is still going (the bar shows days done out of days to run; trades and numbers
appear when it finishes). complete: finished. failed: stopped with an error (the reason is in the
caveats).
- Data mode and fill model
- minute runs use intraday option-chain snapshots; eod runs use end-of-day bhavcopy prices only.
The fill model says which price a trade was assumed to get: bid_ask (sell at the bid, buy at the ask),
mid, or close_haircut (the day's close adjusted against us).