Skip to content

feat: add Zerodha Kite Connect broker support for Indian equities - #1193

Open
ashutoshsinghpr7 wants to merge 6 commits into
HKUDS:mainfrom
ashutoshsinghpr7:feat/zerodha-kite-connect
Open

feat: add Zerodha Kite Connect broker support for Indian equities#1193
ashutoshsinghpr7 wants to merge 6 commits into
HKUDS:mainfrom
ashutoshsinghpr7:feat/zerodha-kite-connect

Conversation

@ashutoshsinghpr7

@ashutoshsinghpr7 ashutoshsinghpr7 commented Aug 24, 2026

Copy link
Copy Markdown

Summary

Resolves #1191. Adds Zerodha Kite Connect as a third India-equity broker alongside Shoonya and Dhan, so the india_equity backtest can be fed Kite historical data (and live read paths exposed behind the existing paper-only / default-deny gates).

Changes

  • New connector agent/src/trading/connectors/zerodha/ (__init__.py, sdk.py, classification.py, profiles.py):
    • get_historical_bars(symbol, *, exchange, period, limit) via kiteconnect, with project-interval to Kite-interval mapping (1m/5m/15m/30m/1h/4h/1d), 2000-day pagination (Kite hard cap), and IST to UTC index normalization matching the existing loaders.
    • Symbol mapping RELIANCE.NS / 500325.BO to Kite exchange=NSE/BSE, tradingsymbol=RELIANCE; instrument token resolved via kite.instruments.
    • Opt-in requires_auth (needs an access token); paper-only order guard (Kite has no sandbox), so live order placement is structurally refused.
  • Loader wiring: india_broker_loader._resolve_broker() now prefers zerodha (then shoonya, then dhan).
  • Profile registry: trading/profiles.py gains the three Zerodha profiles; live/registry.py gains the curated READ/WRITE classification (default-deny).
  • Tests: test_zerodha_broker_loader.py (discovery, symbol/exchange mapping, envelope parse + window clip).

Validation

  • pytest agent/tests/test_zerodha_broker_loader.py agent/tests/test_india_broker_loader.py -> 11 passed, no regressions.
  • zerodha_available() correctly degrades to False when kiteconnect is absent (loader stays inert in CI/unconfigured runs).
  • Module + profile-registry imports verified.
  • Live end-to-end (read-only, real Kite account): get_quote('RELIANCE', NSE) returned live LTP; get_historical_bars('RELIANCE', NSE, 1d) returned real daily OHLCV+volume; get_historical_bars('INFY', NSE, 1m) returned intraday bars (interval mapping works). All read-only calls — no order-placement path was exercised (the connector structurally refuses non-paper orders).

Privacy / no credentials in this PR

This diff contains no real credentials, tokens, client IDs, or personal account data — only code identifiers (api_key, access_token, etc.). Connector config lives in a user-level zerodha.json (0600 perms, git-ignored) and is never committed.

Notes for maintainers

  • kiteconnect is an optional dependency (not added to core requirements); the connector imports defensively.
  • Historical data requires a valid Kite access token; the connector refuses to mint one headlessly (Kite needs an interactive request-token exchange), so users supply access_token directly in zerodha.json.

Happy to adjust to repo conventions if any differ.

- New connector at agent/src/trading/connectors/zerodha/{__init__,sdk,classification,profiles}.py
  implementing the read path (get_historical_bars) via kiteconnect, with
  interval mapping, 2000-day pagination, and IST->UTC normalization.
- Wire zerodha into the india_broker loader _resolve_broker() so the
  india_equity backtest can be fed Kite data (opt-in, requires_auth).
- Register zerodha profiles in trading/profiles.py and live classification in
  live/registry.py (paper-only order guard, default-deny live gate).
- Add test_zerodha_broker_loader.py (discovery, symbol/exchange mapping,
  envelope parse + window clip). All 11 broker-loader tests pass.

Resolves upstream feature request HKUDS#1192.
@ashutoshsinghpr7

Copy link
Copy Markdown
Author

cc @santhreal — you built the Dhan integration and the india_broker loader; would appreciate your eyes on this Zerodha connector.

It follows the same pattern as your Dhan/Shoonya connectors (sdk.get_historical_bars envelope, _resolve_broker preference order zerodha -> shoonya -> dhan, default-deny classification). Tested live read-only against a real Kite account: quote + daily + 1m bars all return correctly.

Specific things I'd value your review on:

  1. The interval map (1h->60minute, 4h->day aliases) — matches what the project expects?
  2. The 2000-day pagination loop in get_historical_bars — any edge cases you hit with Shoonya/Dhan spans?
  3. Should zerodha sit ahead of shoonya in _resolve_broker when both SDKs are installed?

@santhreal

Copy link
Copy Markdown
Contributor
  • agent/src/trading/connectors/zerodha/sdk.py:zerodha_available(): only checks package import only. it is missing cred check which blocks broker fallback.
  • agent/src/trading/connectors/zerodha/sdk.py:_symbol_to_token(): uncached kite.instruments() call re-downloads full exchange dump on every fetch.
  • agent/src/trading/connectors/zerodha/sdk.py:get_historical_bars(): chunk_start = chunk_end loop produces duplicate boundary bars across chunks.
  • agent/src/trading/connectors/zerodha/sdk.py:get_historical_bars(): hardcoded 5-day intraday lookback truncates 1h requests when limit > ~30.

it was only a surface level review. I think @warren618 will provide a deeper review and decide if these are worth fixing and whether the overall pr is fine.

@santhreal

Copy link
Copy Markdown
Contributor

The interval map (1h->60minute, 4h->day aliases) — matches what the project expects?

i think so

…closed, interval-aware pagination, cred-gated availability, cached instruments, service-layer surface

Addresses the review findings on PR HKUDS#1193:
- day bars keep their IST calendar date instead of shifting -1 day to UTC
- 4h rejected (fail closed) instead of silently returning daily candles
- per-interval Kite request caps (1m 60d / 5m 100d / 15m·30m 200d / 60m 400d / day 2000d); 1h window no longer truncated to 5 days
- pagination advances past the inclusive chunk boundary (no duplicate bars)
- zerodha_available() gates on credentials so an unconfigured install never shadows Shoonya/Dhan
- instrument dump cached per exchange; token resolved once per fetch
- service.py broker_sdk dispatch entry + get_account_snapshot/get_positions/get_open_orders/cancel_order and service-surface place_order kwargs
- unit tests for all fixed behaviors
@ashutoshsinghpr7

Copy link
Copy Markdown
Author

Thanks for the review @santhreal — all four points are addressed in the updated branch, plus a few more found in a deeper pass:

  1. zerodha_available() now checks credentials, not just the import — it returns True only when kiteconnect is importable and api_key + access_token are configured, matching the loader's documented "SDK importable AND broker configured" contract. An unconfigured install can no longer shadow Shoonya/Dhan in _resolve_broker.

  2. Instrument dump is cached per exchange — _symbol_to_token fetches kite.instruments() once per exchange (process-level cache) and the token is resolved once before the pagination loop, so no re-download per chunk. The swallow-all except is gone too — a fetch failure now surfaces the real API error instead of a misleading "instrument token not found".

  3. No more duplicate boundary bars — Kite's from/to are inclusive, so each chunk now starts the day after the previous chunk's end (chunk_start = chunk_end + 1 day). Regression test included.

  4. Intraday window is interval-aware — the hardcoded 5-day lookback is replaced with Kite's documented per-request caps (1m 60d / 5m 100d / 15m·30m 200d / 60m 400d / day 2000d) and a limit×2 window, so 1h requests are no longer truncated at ~22 bars.

Also fixed in the same pass:

Day-bar timezone: Kite day candles (midnight IST) keep their IST calendar date instead of shifting −1 day to UTC, so the loader's date-range clip no longer drops the first day of a window.
4h now fails closed instead of aliasing to day (which silently returned daily candles labeled 4h).
Service-layer surface: added the service.py broker_sdk dispatch entry plus get_account_snapshot / get_positions / get_open_orders / cancel_order and the notional/time_in_force kwargs on place_order — the connector now works from the CLI/agent layer, not just the backtest loader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Zerodha Kite Connect broker support for Indian equities

2 participants