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.

Week 3 of 12
Identifiers · holidays · trading-day DTE
Hands-on: your own instrument table
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)

  1. List the 3 to 5 instruments you actually trade or want to trade. Equities, indices, commodities — whatever you touch.
  2. For each one, fill in three columns: display name, exchange + segment, lot size. Look up real values — don't guess.
  3. Ask Claude for the canonical Upstox instrument_key for each. Verify one of them against the Upstox terminal or API.
  4. 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.

source: cowork/Course_Outline_12_Weeks · Week 3
← → navigate · F fullscreen · click to advance
1 / 13