Underlying-agnostic day-by-day backtesting engine for NSE F&O options strategies (pluggable strike selection, delta-drift rebalancing, multi-cycle profit-target/stop-loss exits), backed by a DuckDB cache over the Nubra API. Built to sit behind the options-backtest Claude Code skill.
Project description
NIFTY Options Delta-Neutral Backtesting
A Claude Code skill (.claude/skills/options-backtest/) plus the Python
engine behind it (backtest_engine/) that backtests an NSE F&O options
strategy — any underlying, any legs, any rebalance/exit rule — described to
Claude in plain English. It reports what date ranges are actually testable
before running anything, auto-fetches whatever historical data is missing
from the Nubra API, and caches everything locally in DuckDB so you never
re-fetch the same thing twice.
The skill itself is not NIFTY-specific — the first time it sees a new underlying (BANKNIFTY, a stock, anything on NSE F&O) it discovers that underlying's own data floors from scratch, the same way NIFTY's were originally discovered. What is NIFTY-specific is the dataset that ships in this repo (see below) — a head start, not a limitation of the skill.
What's in this repo
| Path | What it is |
|---|---|
.claude/skills/options-backtest/SKILL.md |
The skill itself — read this first if you want to understand how Claude uses the engine |
backtest_engine/ |
The Python library (distribution name nubra-backtest-engine): strategy definition API, the day-by-day simulation engine, data fetching/caching, capability checks. Pip-installable and independent of this repo's layout — import backtest_engine works from any project once installed; see Setup below |
pyproject.toml |
Packaging metadata for nubra-backtest-engine |
data/market_data.duckdb |
Pre-fetched NIFTY data — see coverage below |
webapp/ |
Optional browser UI: a real terminal (via node-pty + xterm.js) plus a live read-only dashboard of backfill progress / cached data / results |
auth.py |
One-time interactive Nubra login — run this before anything else |
Not included (gitignored, you create your own): .env (your phone
number + MPIN) and auth_data.db* (your saved session token). Never commit
either — see auth.py's own docstring.
Data that ships with this repo
Cached in data/market_data.duckdb as of this push:
- NIFTY: 71 expiries (weekly + monthly), 2025-03-27 through 2026-07-28.
- Option/futures/index OHLC available from 2025-03-24 onward.
- Greeks (delta/gamma/theta/vega/IV) available from 2026-06-01/02 onward — this floor is independent of the OHLC floor, don't assume they coincide. A rebalance policy that needs delta (e.g. roll-to-ATM on delta drift) will refuse to run on an expiry before this floor rather than silently never rebalancing.
- BANKNIFTY: one expiry (2026-07-28) cached from an earlier cold-start validation test — everything else about BANKNIFTY (or any other underlying) needs fresh discovery/backfill, same as a completely new project would.
These floors were empirically discovered, not configured — ask the skill
and it will re-confirm them live from db.get_expiry_metadata() rather
than trusting this table blindly (data may have been backfilled further
since this README was last updated).
Setup
- Install the
backtest_enginepackage:- Not yet on PyPI:
pip install git+https://github.com/aaryan-say/nifty-options-delta-neutral-backtest.git - (Once published, this becomes
pip install nubra-backtest-engine— check PyPI, this note may be stale by the time you read it.) - Working from a checkout of this exact repo instead?
pip install -e .from the repo root does the same thing, editable.
- Not yet on PyPI:
- Install the Claude Code CLI (
claudeon PATH). Node.js 18+ only if you also want the webapp. cp .env.example .env, then fill in your real NubraPHONE_NOandMPIN.- Run
py auth.pyonce, interactively, from this directory — it OTPs you and saves a reusable session toauth_data.db. Every later script/skill run must also be launched from this same directory so it can find that file (and sobacktest_engine's DuckDB cache lands in this project'sdata/, not some other project's). - Launch
claudefrom this directory (orcd webapp && npm install && npm startfor the browser UI), and paste the starter prompt below.
Starter prompt
Paste this as your first message in a fresh claude session, launched
from this repo's root directory:
I'm using this repo's options-backtest skill. Before we discuss any
strategy, do the following:
1. Check backtest_engine/db.py's cached data (expiry_metadata,
intraday_metadata) and tell me exactly what's available right now:
which underlyings, which expiries, the OHLC floor and the Greeks
floor (they're independent — report both), and any known gaps.
2. Confirm my Nubra authentication is actually working (auth_data.db
present and valid) before assuming any live fetch will succeed.
3. Tell me plainly: every fetch you make from here on gets cached
locally in DuckDB automatically, in the same structured tables as
what's already there, for reuse in future backtests -- that's how
this system is built, there's no separate "fetch without saving"
mode. Confirm with me that's fine before you fetch anything new.
4. Ask me whether I want you to proactively pull additional data now
(tell me for which underlying and date range), or whether you
should wait until I describe a strategy and then fetch exactly
what that strategy needs -- either is fine, but ask, don't assume.
5. Then ask me for my strategy in plain English. Map it to a
StrategyDefinition, check capability BEFORE fetching or running
anything, fetch only what's missing, run it, and report results
with timeframe, P&L, number of rebalances, and exit reason. If
anything about the strategy is ambiguous (exit-policy basis,
which Support/Resistance method, rebalance threshold, which
underlying), ask me -- never silently default.
Known limitations to always keep in mind
- Greeks and OHLC have different floors, for every underlying, not just NIFTY — never assume they coincide.
- Intraday option bars have been observed to stop publishing the day
before expiry (confirmed for NIFTY at 5m) — check
capability.check_intraday_capability()before trusting an intraday backtest's last day. - Exit-policy
basis("premium" / "underlying_move" / "capital") changes the answer completely — the skill is instructed to always confirm this with you rather than default to one. capital_proxy(net premium collected/paid) is a stand-in for real exchange margin, not the same thing — real SPAN margin needs a registered static IP on the Nubra account (margin.py), and may be blocked entirely depending on your setup.- Option prices from the historical API come back in paise, already
handled (
/100) indata_client.py— if you ever add a new raw API call path, verify against a live quote first.
Publishing nubra-backtest-engine to PyPI (not done yet)
The package builds and installs correctly (pip install -e . and
pip install git+<this repo URL> are both tested working — import
backtest_engine resolves independent of the repo checkout, and its
DuckDB cache path resolves relative to wherever you run it from, not the
package's install location) but it is not yet published to real PyPI.
No agent can do the actual publish step — it needs a human's own PyPI
account and API token. To finish it:
- Create a PyPI account at https://pypi.org/account/register/ (email verification + 2FA are mandatory for new accounts).
- If the goal is publishing under the same account/org as other
nubra-*packages (e.g.nubra-talib, by Akshay N/githubakshayn-spec) rather than a fresh standalone account: that requires whoever administers that PyPI project (or a shared Zanskar PyPI Organization, if one exists) to add you as a collaborator/maintainer, or to invite you into the org. That's a request to make internally — it isn't something achievable from here. - Once you have publishing rights (solo or shared), generate a scoped
API token from PyPI account settings, then from the repo root:
Twine will prompt for a username/password — usepip install build twine py -m build py -m twine upload dist/*__token__as the username and the API token (including thepypi-prefix) as the password. Never paste that token into a Claude Code conversation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nubra_backtest_engine-0.1.0.tar.gz.
File metadata
- Download URL: nubra_backtest_engine-0.1.0.tar.gz
- Upload date:
- Size: 56.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31786e06548cb11f0780159207e1482e30640bd3c7ad02850c0fe7df9ed85155
|
|
| MD5 |
9608e5ad4113d98f5271aa54691e17f8
|
|
| BLAKE2b-256 |
7ec3179273a1f241baf12d27eb67cde2c4b46d00709b9d8a8341f21e339270f0
|
File details
Details for the file nubra_backtest_engine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nubra_backtest_engine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 64.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9457b270693ddcf4e3034119d5a70ec962f112a7910d2930218d4ab7daf160f1
|
|
| MD5 |
ace7f766532d119893cb243e5cd7c632
|
|
| BLAKE2b-256 |
cf577f6c592a039f16661613279f760796d3e3f02ebc39c31d0c274f582f2418
|