Skip to content

fix(dhan): symbol parsing + recompute fees for unsettled today rows#4

Merged
SwathiMystery merged 1 commit intomainfrom
fix/symbol-fees
Apr 20, 2026
Merged

fix(dhan): symbol parsing + recompute fees for unsettled today rows#4
SwathiMystery merged 1 commit intomainfrom
fix/symbol-fees

Conversation

@SwathiMystery
Copy link
Copy Markdown
Contributor

What

Running the first sync against a live Dhan account yesterday surfaced four more shape differences between today's /trades and history /trades/{from}/{to}/{page} endpoints. This PR fixes all of them.

Why

Observed on live data (04-20 trade):

field today (/trades) history pages
tradingSymbol NIFTY-Apr2026-24300-PE n/a
customSymbol null NIFTY 21 APR 24300 PUT
instrument (absent) OPTIDX
drvOptionType "NA" "PUT"
fee fields (all absent) populated

Left unfixed, today's trades were being stored with underlying="NIFTY-Apr", instrument_type=EQ, option_type=None, and fees=0. Round-trip grouping was broken (contracts inflated by 1) and net P&L was missing ~₹700 per scalp.

Changes

  • mapper.py
    • _underlying_from_symbol: stops at digit, hyphen, or whitespace — covers all three symbol formats (concat / space-sep / hyphenated).
    • _option_type_from_symbol: new helper, parses -CE/-PE/CALL/PUT suffix off the symbol.
    • _infer_instrument_type: new helper. Priority: explicit instrument → option markers → FNO segment → EQ.
    • _OPTION_TYPE: add "NA"None so the map lookup gates cleanly into the fallback.
  • fees.py (new): Indian F&O options fee formula — brokerage (Dhan ₹20 flat or 0.03%), STT (0.0625% SELL only), NSE txn (0.03503%), SEBI (₹10/cr), stamp (0.003% BUY only), IPFT, GST 18%. Rates documented inline with source notes.
  • adapter.py
    • New charges_for(execution) → dispatches to fees.compute_fees. Returns None for non-options (futures/equity formulas differ, out of scope here).
    • fetch_trades recomputes fees whenever the broker returns a zero-fee row.
  • Fixtures + tests: trades_today_unsettled.json mirrors today's no-fees/NA-option shape. 7 new test cases covering each fix and the happy-path non-regression.

Verification

Against a live account:

before:
  04-20  NIFTY-Apr2026-24300-PE  LONG  -₹42,812.25  fees=₹0.00       underlying=NIFTY-Apr

after:
  04-20  NIFTY-Apr2026-24300-PE  LONG  -₹43,527.57  fees=₹715.32    underlying=NIFTY

Contracts count went 17 → 16 as the 04-20 trade now correctly groups with NIFTY underlying.

Checklist

  • Tests added (7 new cases; 22 total; all green locally)
  • uv run ruff check khata tests clean
  • uv run ruff format khata tests applied
  • Docs: fee formula sources documented inline in fees.py
  • Fixtures use synthetic IDs only (TEST_*) — no real account data
  • No schema change

Running against a live account exposed three more shape quirks in
Dhan v2's /trades (today) endpoint vs /trades/{from}/{to}/{page} (history):

1. Hyphenated tradingSymbol on today's rows
   'NIFTY-Apr2026-24300-PE' was resolving underlying to 'NIFTY-Apr'
   because the parser only stopped at digits. Now stops at digit,
   hyphen, or whitespace — handles all three formats observed:
     concat:       NIFTY24APR2624300PE
     space-sep:    NIFTY 21 APR 24300 PUT
     hyphenated:   NIFTY-Apr2026-24300-PE

2. drvOptionType='NA' on today's rows
   Today's /trades leaves drvOptionType='NA' even when the tradingSymbol
   clearly ends '-PE' / '-CE'. Added _option_type_from_symbol fallback
   that reads the suffix.

3. `instrument` field missing entirely on today's rows
   Every row defaulted to InstrumentType.EQ, which blocked the new
   option_type fallback and the fee recompute below. Added
   _infer_instrument_type: explicit field → option markers → FNO
   segment → EQ.

4. Fees absent on today's rows (not a mapping bug, real data gap)
   Today's /trades omits fee fields entirely — they settle at EOD.
   Added khata/adapters/dhan/fees.py with Indian F&O options fee
   formula (brokerage flat ₹20 or 0.03% whichever lower, STT 0.0625%
   on SELL, NSE exch 0.03503%, SEBI ₹10/crore, stamp 0.003% on BUY,
   IPFT 0.0005%, GST 18%). Exposed via DhanAdapter.charges_for, called
   from fetch_trades when broker-reported fees total zero.

Tests: 7 new cases (22 total, all green). Fixtures use synthetic IDs
only. Verified against live account: today's 04-20 trade went from
fees=₹0.00 to ₹715.32 and underlying 'NIFTY-Apr' to 'NIFTY'.
@SwathiMystery SwathiMystery merged commit 0de760a into main Apr 20, 2026
7 checks passed
@SwathiMystery SwathiMystery deleted the fix/symbol-fees branch April 20, 2026 19:29
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.

1 participant