Week 03 · Build Your Own Automated Options Trading System
Markets,
the way software sees them.
A human says "NIFTY". The exchange says NSE_INDEX|Nifty 50. The bot has to know both — and it has to know which day is a holiday before it ever places an order.
This week, in one glance
Same five beats. New topic.
1
The idea
Software is pedantic about IDs, timestamps, and which holiday file belongs to which exchange.
2
The outcome
You know why this codebase always means trading-day DTE — and you can tell display symbol from instrument_id.
3
New vocabulary
instrument_id · JSON config · primary key vs display name · dictionary lookup · canonical identifier
4
Hands-on
Build a small table of your instruments with their canonical IDs.
5
War story
A wrong holiday file once put a strategy live on a closed exchange. "Is today a trading day?" is now line one.
1 · The idea
You say "NIFTY." Software needs more.
How a human reads a market
- "Buy 1 lot of NIFTY 24800 CE for this week."
- "The market is closed tomorrow — it's a holiday, right?"
- "NIFTY expiry is Tuesday. Or Thursday. One of those."
- "It's 2 days to expiry, give or take a weekend."
- Vague is fine — your brain fills the gaps.
How software reads a market
- Place order for
NSE_FO|46051, qty=65 (1 lot × 65), product=NRML.
is_trading_day("2026-05-15") → True / False. Boolean. No "right?"
- NIFTY weekly expiry is Tuesday. SENSEX is Thursday. The code never confuses them.
- DTE = 2 means 2 trading days away — weekends and holidays excluded.
- Vague is a bug. Numbers, IDs, timestamps. Always.
Half the bugs in any trading system live in the gap between what you say and what software actually needs.
Display name vs canonical identifier
"NIFTY" is for humans. NSE_FO|46051 is for orders.
| What you say |
What the order needs (instrument_id) |
Display / friendly name |
| NIFTY (the index, for charts) |
NSE_INDEX|Nifty 50 |
NIFTY 50 |
| "This week's 24800 CE" |
NSE_FO|46051 |
NIFTY 20MAY26 24800 CE |
| SENSEX option leg |
BSE_FO|871234 |
SENSEX 22MAY26 80000 PE |
| "Crude near-month" |
MCX_FO|261540 |
CRUDEOIL 21MAY26 6200 CE |
The display name changes every week. The instrument_id is the primary key — the one fact the order absolutely needs right.
"Two days to expiry" — but which two?
This codebase always means trading-day DTE.
Calendar DTE is wrong here. If today is Friday and NIFTY expires Tuesday, that's a 2-day DTE in this codebase — Saturday and Sunday don't count.
Calendar DTE (wrong here)
Counts every day, including weekends and holidays. Misleading because the market is closed on those days.
# Friday 16 May → Tuesday 20 May expiry
# Calendar DTE = 4 (Sat, Sun, Mon, Tue)
dte = (expiry - today).days # ❌ wrong
Trading-day DTE (the one we use)
Counts only days the exchange is open. Holiday file is consulted. This is what the strategies key off.
# Friday 16 May → Tuesday 20 May expiry
# Trading DTE = 2 (Mon, Tue)
dte = compute_trading_dte(today, expiry)
"DTE in this project always means trading-day DTE." Pin that to your forehead.
2 · By the end of this week
Four things you can do without thinking.
You can read an instrument_id like NSE_FO|46051 and know it's a primary key — not a display name.
You can explain why a strategy must check is_trading_day(today) as its very first line, before doing anything else.
You know what a JSON config file is for: a list of facts the code looks up but never invents.
You can say "DTE" and mean trading-day DTE, every time, without ambiguity.
No code written yet. The skill is the vocabulary and the discipline.
3 · New vocabulary
Five words. Use them this week.
instrument_id
The exact identifier the broker needs to place an order on a specific contract. Globally unique.
NSE_FO|46051
JSON config
A plain text file holding facts (holidays, lot sizes, expiries) the code reads — never invents.
nse_holidays.json
primary key vs display name
The ID the code keys off, vs. the friendly label shown to a human. Don't mix them up.
46051 vs "NIFTY 24800 CE"
dictionary lookup
"Given a key, give me the value." Constant-time. The bread and butter of every config read.
cfg["NIFTY"]["lot_size"]
canonical identifier
The one official form everyone agrees on. There can be many display names; the canonical ID is unique.
NSE_INDEX|Nifty 50
Drop one of these into a sentence per day this week and they'll stick.
The shape of a JSON config
A list of facts. Not a place for guesses.
config/nse_holidays.json
"facts the code reads, never invents"
{
"_meta": "Holidays for both NSE and BSE. Both exchanges share the same calendar.",
"2026": [
"2026-01-26", // Republic Day
"2026-03-26", // Holi
"2026-05-01", // May Day
"2026-08-15", // Independence Day
"2026-10-02", // Gandhi Jayanti
"2026-11-10", // Diwali (Laxmi Pujan)
"2026-12-25" // Christmas
]
}
Verify this list against the NSE calendar every January. Don't trust the model to fill it in.
4 · The hands-on bit — your task
Build your instrument table.
What to do this week (45 minutes, paper or spreadsheet)
- List the 3 to 5 instruments you actually trade or want to trade. Equities, indices, commodities — whatever you touch.
- For each one, fill in three columns: display name, exchange + segment, lot size. Look up real values — don't guess.
- Ask Claude for the canonical Upstox
instrument_key for each. Verify one of them against the Upstox terminal or API.
- Mark the column you would use to place an order. Mark the column you would use to draw a chart. They are different.
| Display name |
Exchange · segment |
Lot size |
instrument_id |
| NIFTY 50 (index) |
NSE · INDEX |
— |
NSE_INDEX|Nifty 50 |
| SENSEX option (weekly) |
BSE · FO |
20 |
BSE_FO|… |
| CRUDEOIL near-month |
MCX · FO |
100 |
MCX_FO|… |
If you can't find an exact instrument_id, leave it blank — don't fabricate one. "I don't know yet" is a valid cell.
5 · A real-money war story
A wrong holiday file put a strategy live on a closed exchange.
The holiday JSON was missing a date. The strategy launched at 09:15. The exchange was shut. The bot kept retrying, eating connections, raising alerts. No order filled, but the operator's morning was on fire.
What happened
Holiday file was last edited in January. Someone added a mid-year holiday to the exchange calendar later. The file never got the update.
# nse_holidays.json — stale
"2026": [
"2026-01-26",
"2026-03-26",
// missing "2026-05-01" — May Day
...
]
# strategy started → market closed → chaos
What we do now
Every strategy's first line, before anything else, is: is today a trading day? If not, log clean and exit. Holiday file is reviewed every January.
# first line of every strategy
if not is_trading_day(today):
log.info("non-trading day, exiting clean")
sys.exit(0)
# exit code 0 = "did nothing on purpose"
The lesson, pinned
"Is today a trading day?" is line one.
1
missing date in a JSON file
7
strategies need their own holiday check today
100%
of strategies start with is_trading_day() now
Configuration data is a real artifact. It rots. Holiday lists, lot sizes, expiry overrides — they all
drift away from reality if nobody refreshes them. The defence is two-part:
(1) every strategy must check the calendar before doing anything;
(2) the calendar files get a yearly audit against the official exchange schedule, by a human, not a model.
Stale data is a bug. Pretending it isn't is the worst bug of all.
Week 3 — takeaway
Software sees IDs and dates,
not stories.
If you give a bot a vague identifier or a stale calendar, it will trade the wrong thing on the wrong day with the right amount of confidence. The cure is precision: canonical IDs, verified config files, trading-day DTE — every time.
End of Week 3
Next: Talking to the broker.
Week 4 — Tokens, sessions, and the first real API call →
source: cowork/Course_Outline_12_Weeks · Week 3