Repository navigation
indian-algo-trading v1.2.0
Changelog
All notable changes to this project will be documented in this file.
Format follows Keep a Changelog.
This project uses Semantic Versioning.
[1.2.0] - 2026-08-28
Added
- Critical Rule 16 — "Cash-equity intraday exits must clear the Closing Auction Session." SEBI's CAS framework went live in the cash segment on 3 August 2026 for securities with live derivative contracts. For those symbols continuous trading now ends at 3:15 PM, not 3:30, and the closing price is set by a call auction: no order entry 3:15-3:20, limit + market 3:20-3:25, limit only from 3:25 to a system-driven random close between 3:28 and 3:30, matching 3:30-3:35, post-close moved to 3:50-4:00. The equity derivatives segment was extended to 3:40 PM. The skill had no awareness of any of this and would generate strategies that place exit orders into a book that no longer exists.
references/indian-market.md§1A "Closing Auction Session (CAS)" — full phase timeline, the ±3% static reference-price band (VWAP of 3:00-3:15, a 15-minute window), order-type eligibility table, carry-forward and time-priority rules, equilibrium-price and matching-priority logic, per-exchange divergence, suspension cases, and an explicit broker-layer section. Sourced only to SEBI circularSEBI/HO/47/11/11(3)2025-MRD-POD2/I/2765/2026(16 Jan 2026),NSE/CMTR/73362(18 Mar 2026), NSE CAS FAQ v1.0 (May 2026) and BSE Notice 20260610-41 (10 Jun 2026) — several broker and news explainers published the wrong session length after go-live.- Runtime CAS-eligibility derivation for Vortex (
client.instruments.all_by_underlying("NSE_FO", underlying)), since the SDK exposes no CAS flag and the eligible list moves with SEBI's F&O reviews. - "Bar gaps on CAS scrips" (
indian-market.md§1A) and CAS discontinuities inbacktesting.md. There are no candles at all for a CAS scrip between 3:15 and 3:30 PM — not empty bars, none — because no continuous matching occurs. Every consequence is a silent failure:df.iloc[-1]at 3:22 PM returns the 3:14 bar so "current price" is stale, "wait for the next bar" loops never fire, fixed bar-count lookbacks shift meaning, daily gap-detection alerts fire falsely, andffill()fabricates prices that never traded. A CAS scrip yields ~360 one-minute bars a day against 375 for a non-CAS symbol, so multi-symbol panels must join on timestamp rather than position. The guidance is to drive all closing-window logic offdatetime.now(IST), never off bar arrival. - "Who You Are Talking To" section in
SKILL.md— states the reader's user is a trader, not a programmer, and derives the consequences (answerable questions, safe defaults instead of blocking, rupees not bare percentages, walk-throughs for anything the user must do themselves). - Mandatory pre-handover validation gate —
scripts/validate_strategy.pyshipped since 1.0 and was referenced from nowhere inSKILL.md. It is now a required step in Strategy Output Format, with guidance on interpreting its per-file heuristics and a note that it does not cover the silent-until-live rules. - "Explaining the result" section — what the README and the post-generation explanation must contain, since the README is the only artifact a non-programmer can read.
Changed
- Market hours are now segment-split everywhere.
indian-market.md(timings table, F&O bullet, segments table, DPR section, short-selling section, launch checklist, footer),risk-management.md(should_exit_on_timerewritten to take asegment, stop-loss section warning, key takeaways),execution-alpha.md(seasonality buckets, VWAP volume profile, iceberg warning, closing-window state machine),strategy-patterns.md(VWAP operating hours, IST market-hours block, stop-loss discipline). - Rules 3, 9, 10 and 12 gained CAS caveats. Untriggered stop-loss and disclosed-quantity orders are cancelled by the exchange at 3:15 PM on CAS scrips, so a resting broker-side SL is not protection through the close (Rule 3) and the resulting burst of exchange-initiated
CANCELLEDpostbacks must not be read as user cancellations (Rule 9). The auction's ±3% band is tighter than DPR and binds any auction-routed order (Rule 12). - Rule 16 added to the silent-until-live list (now Rules 1, 11, 12, 13, 15, 16).
- Step 1 question 4 (deployment) rewritten in plain language with a default, so a non-programmer can answer it; question 5 (risk) now asks in rupees. Step 2 now requires confirming the strategy back to the user in plain English before writing code.
- Scaffolded
config.py: the unusedmarket_close_time = "15:30"replaced bycontinuous_end_cash_cas/continuous_end_cash_non_cas/continuous_end_derivativesplusintraday_exit_buffer_minutes. - Graceful Shutdown now requires a session-aware square-off path that cannot report success on an unfilled exit.
Fixed
- Generated
main.pycrashed withNameErroron every scaffold. The template emittedif "live" in LIVE:—LIVEis a bare undefined name, produced by interpolatingstrategy_type.upper()into the generated source. The branches were also inverted: thelivepath calledstrategy.backtest()and vice versa. Replaced with a literal branch resolved at scaffold time. It passedpy_compile, somake test-scaffold(which only checks that files exist) never caught it. - Scaffolded
Strategy.run()/.backtest()exited silently. Both stubs logged "not yet implemented" and returned, sopython main.pycompleted with exit code 0 on a strategy that had no logic — indistinguishable from a working run for a non-programmer. They now raiseNotImplementedErrornaming the fix. - The
.pluginpackage shipped noscripts/directory at all.SKILL.mdinstructs Claude to runscripts/scaffold_strategy.py(Rule 8) andscripts/validate_strategy.py(the new handover gate), andCONTRIBUTING_BROKER.mdpoints atscripts/validate_broker_adapter.py— none of which existed for anyone who installed via the marketplace plugin, only for.skillinstalls. The Makefile'splugintarget now stagesscripts/*.py, andSKILL_FILESuses a wildcard so a newly added script can't silently fall out of both packages again. BROKER_TEMPLATE.mdtold broker contributors to validate their adapter withvalidate_strategy.py— the AST linter for generated strategy code, which cannot check a markdown adapter doc. Corrected toscripts/validate_broker_adapter.py.validate_strategy.py's timezone check passed on the exact bug Rule 7 warns about. The condition was'utc' in source.lower(), so a strategy callingdatetime.utcnow()— naive UTC, 5h30m off IST — satisfied the TIMEZONE check. It now looks for a real IST setup (pytz/ZoneInfo/Asia/Kolkata/astimezone) and adds aNAIVE_DATETIMEcheck that flagsdatetime.utcnow(),datetime.now()anddate.today()by name.indian-market.mdclaimed intraday positions are "forcibly closed by the exchange" at 3:29:59. Square-off is the broker's RMS on its own schedule; published cutoffs range from about 3:00 to 3:12 PM for CAS scrips and are policy, not regulation. Now documented as broker-specific and never to be hardcoded.risk-management.mdstated a "4:00 PM NSE close". NSE continuous trading has never ended at 4:00 PM.strategy-patterns.mdreferred to "choppy 10-4 PM sessions" — no such session exists.- Post-close session corrected from 3:40-4:00 to 3:50-4:00 (it moved for CAS and non-CAS securities alike).
Notes
- The Pre-Open Auction Session realignment is effective 7 September 2026 and is not yet in force. Documented in full as
indian-market.md§1B: limit+market 9:00-9:05; limit only 9:05-9:10 with a system-driven random close in the last 2 minutes (9:08-9:10); matching 9:10-9:12; transition to CTS 9:12-9:15. It applies to the whole cash segment, not just CAS scrips, and it retires the 9:08 AM order-entry cutoff. Code touching pre-open must branch on the date; a backtest spanning the changeover needs both structures. - Rupeezy/Vortex-specific CAS behaviour (order acceptance during 3:15-3:20, square-off timing, GTT triggers, rejection codes) is not covered by any circular and must be confirmed against Rupeezy's developer notes before live use.
- The auction print's exact timestamp, and whether the daily
closefield from a given historical API carries the equilibrium price, still vary by vendor — confirm against your own data source. - Conflicts left deliberately hedged: NSE says IOC is not allowed in CAS while BSE's guidelines describe IOC orders inside CAS; the ±3% stock-futures band is NSE-sourced, not SEBI-sourced; the circuit-breaker CAS suspension and the "Buffer Period" label are BSE-only; the 3:10-3:40 derivatives closing VWAP comes from the NSE FAQ citing a circular that could not be retrieved.
- CHANGELOG gap: entries for 1.1.11 through 1.1.14 were never written; this entry follows 1.1.10.
[1.1.10] - 2026-05-26
Added
OrderTrackerclass (canonical inreferences/code-quality.md; scaffolded asorder_tracker.pyfor live strategies). Postback-driven, refreshes from orderbook + tradebook with 500 ms debounce, fireson_terminal(order_id, status)for every terminal transition. API:initialize,on_update,on_terminal,wait(order_id, timeout)(timeout required),cancel_and_wait,status/order/fills/avg_fill_price.- Scaffolder generates
order_tracker.pyfor--type live; wires it throughmain.py(initialize→on_terminal→ connect feed) and adds anon_order_terminalmethod tostrategy.py(replaces the emptyon_order_fill/on_order_cancelstubs).
Changed
- Critical Rule 9 rewritten — postback is a signal, orderbook is the truth. Names all five envelope types (
order,trade,sl_trigger,gtt_order,position_conversion); strategy code refreshes fromclient.orders()/client.trades()instead of parsingmsg["data"]; postbacks coalesced via 500 ms debounce. Live strategies useon_terminal;wait()reframed as a script/test primitive with requiredtimeout. - Broker reference Common Patterns rewritten around
OrderTracker.on_terminal+initialize();orders()/order_history()callouts now distinguish sleep-loop polling (anti-pattern) from postback-driven refresh (recommended). - SKILL.md halved — 405 → 216 lines, ~5.5K → ~2.8K always-loaded tokens. No rules removed, only verbosity. Step-1, Code Architecture, Rule 8, Output Format, Backtesting Standards, and Proactive Suggestions rewritten as terse bullets.
Fixed
- Skill was teaching order-status polling.
references/code-quality.mdshipped await_for_order_fill()that polledbroker.get_order_status()in awhile time.sleep(0.5)loop, with a docstring rationalising "polling instead of events to avoid callback hell". Rewritten as event-driven; Rule 9 and broker callouts now explicitly forbid sleep-loop polling. - Postback envelope shape. Real payload is
{type, data, client_code}; examples were reading the inner fields at the top level (wouldKeyErroron real traffic). All examples now filter onmsg["type"]and unwrapmsg["data"]. - Thread-start race in
OrderTracker.on_update—try/finallyresets_worker_runningifThread.start()raises, preventing silent permanent refresh stoppage. on_terminaldeduplication via_notified_terminalset; an order staying terminal across multiple refreshes only fires the callback once.- Stale
lookup_token(master, "RELIANCE", "NSE_EQ")snippet in broker reference Common Patterns — nowticker="NSE:RELIANCE". COMPLETEDvsEXECUTEDalias — same state under two names (postback envelope returnsCOMPLETED,client.orders()returnsEXECUTED).OrderTracker.TERMINALandSUCCESSnow include both. Without this, the orderbook refresh would never fireon_terminalfor filled orders.- Postback-vs-REST lag broke the reference
OrderTrackeron rejections (field-test fix). Real-world: WS pushesREJECTEDpostbacks within ms ofplace_orderreturning, butclient.orders()keeps returningPENDINGfor the sameorder_idfor ~30 seconds. The previous "postback = signal, REST = truth" model missed rejections and let stale REST overwrite a terminal status. Reframed as split authority: postbackdata.statusis authoritative for status transitions; REST is authoritative for fill quantities / prices.OrderTracker.on_updatenow fast-paths terminalorderpostbacks (mirrors payload into cache, fireson_terminalsynchronously on the WS thread)._applyhas a downgrade guard: once a terminal status is recorded, a later non-terminal REST row merges fill fields but does NOT overwritestatus. - Substring terminal-status detection. Brokers emit decorated forms (
REJECTED_BY_RMS,AMO_CANCELLED,EXECUTED_PARTIAL). The previous strict set membership check missed these.OrderTracker._terminal_token()substring-matches against canonical tokens and normalises the storedstatusfield; the original verbose form is preserved inraw_status. - Response envelope keys differ per endpoint (field-test fix).
client.orders()returns{"orders": [...]},client.trades()returns{"trades": [...]}— NOT{"data": [...]}(which is the holdings/positions/funds shape).OrderTracker._orders_rows()/_trades_rows()chain through the common key names. Broker reference gains an explicit "Endpoint response envelope keys" table. - SDK signature drift since 2.1.8 (field-test fixes).
client.orders()andclient.trades()now require(limit, offset)positional args (raisesTypeErrorif omitted);OrderTracker._fetch_orderbook/_fetch_tradebooktry the new form and fall back to no-args onTypeError.place_order'sdisclosed_quantityis REQUIRED (broker reference parameter table updated).get_order_marginresponse is flat ({"required_margin", "available_margin", "status"}) — not nested under"data"; broker reference example rewritten to use the flat shape and dropped thefunds()double-call. - Rejection reason field name varies by surface (field-test fix). Postback envelope uses
status_message; REST orderbook row useserror_reason;place_orderresponse usesmessage. AddedOrderTracker.rejection_reason(order_id)accessor that walks the chain. Scaffoldedstrategy.py.on_order_terminaluses it. Broker reference Order Rejection section documents all three field names with a table. - Instrument master quirks (field-test fix).
expiry_dateisYYYYMMDDin the master CSV, ISO in REST orderbook rows, and"29May2026"in postbacks for the same contract. Documented in the broker reference Instrument Master section with a recommendation to lexicographically compare master-CSV expiry strings instead of parsing. Futures rows useoption_type='XX'(sometimes' '), notNone/""— documented the robust filter idiom. funds()unreliable as a margin-availability source (field-test fix). On some accounts it returned{}with no buckets — wouldKeyErroronfunds()["data"]["equity"]["margin_available"]. Broker reference Margin Check example rewritten to readavailable_margindirectly fromget_order_margin's response (one round-trip, stable shape) and explicitly warn against the funds()-double-call pattern.- Scaffolded
config.pynow callsload_dotenv()at module top (field-test fix). Previously a caller importingconfig.pybeforeauth.pywould read empty env vars. config.py uses atry: from dotenv import load_dotenvguard so it's harmless on container deployments.auth.pyno longer duplicates the call; it justimport configfor the side-effect.requirements.txtkeepspython-dotenvin both deployments so the import is always available. Documented that the PyPI package ispython-dotenv(NOTdotenv— separate, unrelated package). - Typed-enum gotcha on
Vc.*constants (field-test fix). The Vortex SDK runtime-typecheckstransaction_type,product,variety,validity,mode,exchange,resolutionvia strictisinstance(value, EnumClass)atvortex_api/api.py:147. Passing the string value (e.g."INTRADAY") raisesTypeError: product must be of type ProductTypeseven though the enum's.valueIS"INTRADAY". Silent until first liveplace_order/get_order_margin—py_compileand unit tests pass. Fixes: scaffoldedconfig.pynow storesdefault_product/default_variety/default_validity/default_transactionasVc.*enum instances (typed annotations + correct defaults), soclient.place_order(product=config.default_product, ...)works as-written. Added new Critical Rule 15 toSKILL.mdand a "Vc.* constants are runtime-typed enums" entry under broker reference "Field-format quirks" with the name-vs-value pitfall called out (Vc.VarietyTypes.REGULAR_LIMIT_ORDER.value == "RL"— name and value differ for many enums). Constants Reference table gained a "pass the enum, not the value" callout.
[1.1.9] - 2026-05-26
Changed
- Eliminated five places where the skill contradicted itself on the loopback OAuth login. A clean read by an LLM consumer could previously interpret the manual auth-code paste flow as compliant. Specifically:
- Step 1 question 4 — "default to the loopback SSO pattern" rewritten as a hard
MUSTwith an explicit decision tree ("self-hosted → ship login.py + auth.py" vs "container → don't"). Headless / SSH-only edge case is called out as non-exempting. - Code Architecture diagram — the
(self-hosted only)parenthetical that read as "optional" replaced with two explicit deployment-keyed file lists labelledREQUIRED. - Critical Rule 8 — promoted from "use the loopback SSO login" (suggestion-flavoured) to "Self-hosted strategies MUST ship login.py + auth.py — no exceptions". Adds an explicit list of patterns that are forbidden (
input("auth code: "),VORTEX_ACCESS_TOKENin.env,broker.pywith a manual auth_code parameter) and rebuts three common edge-case excuses (headless box, "portal might not allow localhost", minimal-deps). - Strategy Output Format (the section LLMs use as the final file checklist) — was omitting
login.pyandauth.pyentirely. Now shows two deployment-keyed file lists; container packages explicitly must not includelogin.py/auth.py. references/brokers/rupeezy-vortex.md— deleted the "Advanced (only when the loopback server can't run)" escape hatch that explicitly told the LLM "headless → fall back to manual paste"; replaced with the SSH port-forward recipe. Collapsed the duplicate "Authentication / Self-Hosted OAuth Flow" subsection that was contradicting the Deployment Modes section above it.
- Step 1 question 4 — "default to the loopback SSO pattern" rewritten as a hard
[1.1.7] - 2026-05-26
Added
- Loopback SSO login pattern for self-hosted strategies. New critical Rule 8 in
SKILL.mdrequires shipping alogin.pythat spins up a stdlibHTTPServeron127.0.0.1:8765/callback, opens the SSO URL in the browser, and exchanges the capturedauth_tokenfor anaccess_tokenautomatically. End users never see either token. Strategies read the cached token viaauth.get_client(). Eliminates the most common end-user bug (confusingauth_tokenwithaccess_token). scripts/scaffold_strategy.pygained a--deployment {self-hosted,container}flag (defaultself-hosted). Self-hosted scaffolds shiplogin.py+auth.py;main.pycallsget_client();.env.examplecarriesVORTEX_API_KEY+VORTEX_APPLICATION_ID;requirements.txtpulls inpython-dotenv. Container scaffolds skip the login files entirely:main.pydoes zero-argVortexAPI()(platform injectsVORTEX_ACCESS_TOKEN),.env.examplewarns against putting broker credentials in.env, andpython-dotenvis dropped fromrequirements.txt. "Next steps" output branches accordingly.references/brokers/rupeezy-vortex.mdSelf-Hosted section rewritten with fulllogin.py+auth.pycode listings; manual OAuth flow demoted to an "advanced/headless only" footnote.
Changed
- Ticker-first guidance for vortex-api >= 2.1.8. Critical Rule 1 in
SKILL.mdand the entirereferences/brokers/rupeezy-vortex.mdreference now teach identifying instruments by ticker ("NSE:RELIANCE") instead of(exchange, token)pairs. Updated examples:place_order(ticker=...),historical_candles(ticker=...),get_order_margin(ticker=...)client.quotes(instruments=["NSE:RELIANCE"], ...)— tickers accepted directlywire.subscribe(ticker=..., mode=...)and readingtick["ticker"]from VortexFeed updatesclient.instruments.get_by_ticker(...)/get_by_exchange_token/get_by_isin/all_by_underlying/filterreplace hand-rolled CSV scanning
- Replaced the broken
from vortex import Client+master[master['tradingsymbol']==…]snippet inreferences/backtesting.mdwith a working ticker-formhistorical_candlescall. references/indian-market.mdtick-size lookup, data-sources table, and "Always download fresh instrument master" sections now point toclient.instrumentsfor Vortex while remaining broker-agnostic.scripts/validate_strategy.py— the "hardcoded token" violation message now recommends the ticker form first, withclient.instruments.get_by_ticker(...)as the metadata-access path.- Documented the IDX ticker convention for indices (
"NSE:NIFTYIDX","NSE:BANKNIFTYIDX","BSE:SENSEXIDX") — the suffix lives on the ticker, the underlying symbol stays bare. - Bumped
requirements.txtexample fromvortex-api>=1.0.0tovortex-api>=2.1.8.
Notes
- Legacy
(exchange, token)form is still accepted by the SDK but emitsFutureWarning. One legacy example is retained per surface (orders, websocket) so users on older code can recognise the deprecated pattern.
[1.1.4] - 2026-03-31
Changed
- Restructured repo to native plugin layout (skill files under
skills/indian-algo-trading/) - Repo is now directly installable as a Cowork marketplace — no build step needed
- Updated Makefile for new directory structure
[1.1.1] - 2026-03-31
Added
marketplace.jsonfor plugin discoverability in marketplaces- Dual packaging:
.skill(platform-agnostic) and.plugin(Claude + Rupeezy MCP) .mcp.jsonbundling Rupeezy Trading and Strategy Platform MCP servers- Makefile with
skill,plugin,all,release,validate, andtest-scaffoldtargets - CONTRIBUTING_BROKER.md — step-by-step guide for adding broker adapters with AI prompt template
validate_broker_adapter.py— automated broker adapter validation script
Changed
- Removed MCP tool docs from
rupeezy-vortex.md(auto-discovered via.mcp.json)
[1.0.0] - 2026-03-31
Added
Core Skill
- SKILL.md with pre-flight checklist, reference routing table, 13 critical rules, and code architecture patterns
- Progressive disclosure: 290-line brain routes to 16 reference files by context
Reference Files (16)
strategy-patterns.md— 6 core + 5 advanced strategy patterns with code skeletonsrisk-management.md— position sizing (fixed fractional, ATR, Kelly Lite), drawdown controls, F&O margin monitoringindian-market.md— market timings, expiry calendar, STT rates FY 2025-26, circuit limits, auction risk, tick sizes, DPR, NSE no-API rulebacktesting.md— library selection guide, realistic transaction costs, parameter optimizationerror-handling.md— order state machine, partial fills, graceful shutdown, state persistencecode-quality.md— project structure, logging, pytest patterns, config managementoptions-greeks.md— Black-Scholes, delta-neutral, gamma scalping, theta harvesting, IV vs RVregime-detection.md— HMM for 3 regimes, strategy decay via rolling Sharpeindia-data-edge.md— FII/DII flows, OI analysis, PCR, max pain, delivery %, rollover, GIFT Niftyexecution-alpha.md— TWAP, VWAP, iceberg, impact cost, NSE intraday timing patternsrobustness-testing.md— walk-forward optimization, Monte Carlo, sensitivity analysisportfolio-construction.md— multi-strategy allocation, correlation-aware sizing, decay rotationpsychological-guardrails.md— daily loss breaker, consecutive loss pause, weekly throttle, killswitchtax-optimization.md— STCG vs LTCG (20% vs 12.5%), tax-loss harvesting, F&O business incomepython-performance.md— vectorization, Numba JIT, Polars, async, profiling workflow
Broker Support
brokers/rupeezy-vortex.md— full Vortex SDK reference (primary broker)brokers/BROKER_TEMPLATE.md— 12-section template for community broker adaptersbrokers/CONTRIBUTING_BROKER.md— step-by-step guide for adding a new broker with AI prompt template, verification checklist, and maintainer review process
Scripts
validate_strategy.py— AST-based linter checking for hardcoded tokens, missing stop-loss, print statements, NSE scraping, tick size roundingvalidate_broker_adapter.py— validates broker adapter docs against template structure, checks for placeholders, constants completeness, OAuth flow documentationscaffold_strategy.py— generates best-practice project skeleton with main.py, strategy.py, risk_manager.py, guardrails.py, config.py, tests/
Assets
assets/strategy_template/— 9 standalone template files matching scaffold output, browsable as reference
Evals
evals/evals.json— 10 test prompts with 65 assertions covering all critical skill capabilities
Governance
- CONTRIBUTING.md with 6 contribution types, trust tiers, DCO, code of conduct
- Strategy patterns are core-team only (not open for community contribution)
- Apache 2.0 license