taxlots
taxlots is a command-line capital-gains engine for Indian investors. Feed it the
tradebook CSVs from your broker (Zerodha, Groww or anything else via a small TOML profile),
an asset master and your corporate actions. It rebuilds every tax lot with FIFO per demat
account, applies the Indian rules by sale date, and produces filing-ready output. That covers
the 23-Jul-2024 rate switch, 31-Jan-2018 grandfathering, indexation, sec 50AA debt funds,
bonus/split/merger adjustments, intraday separation and loss set-off. You get per-lot gains,
ITR Schedule 112A rows, an FY tax summary with set-off and carry-forward, open lots with
their LTCG dates, a tax-gain-harvesting plan and a step-by-step explain for any sale.
Everything runs offline on a local SQLite file.
Features
- Import Zerodha Console tradebooks or any CSV via TOML column profiles; idempotent re-imports (trade-ID or content-fingerprint dedupe).
- FIFO per ISIN per account (sec 45(2A)); optional specific identification for non-demat folios.
- FY-aware rules: holding periods (12/24/36 months), 111A 15 % → 20 %, 112A 10 % → 12.5 %, ₹1 L → ₹1.25 L exemption, 112 20 % indexed → 12.5 % flat, sec 50AA (incl. the gold-ETF change from FY 2025-26), sec 10(38) for pre-2018 sales.
- Grandfathering under sec 55(2)(ac), with the 31-Jan-2018 FMV carried correctly through later splits and mergers.
- Corporate actions: splits, bonus (cost nil, acquired on allotment), mergers/renames (cost and holding period carried).
- CII table FY 2001-02 … FY 2025-26 (376) embedded.
- Charges: buy charges into cost, sell charges as transfer expenses, pro-rata by quantity across lots.
- Intraday same-day equity round-trips split out as speculative business income.
- Set-off (secs 70/74) in a tax-minimising order, mandatory set-off against exempt 112A gains, carry-forward listing with expiry AY; brought-forward losses accepted.
- Tax estimate with 4 % cess and optional surcharge (capped at 15 % on capital-gains rates).
- Tax-gain harvesting planner that respects FIFO.
- Output as rich tables, CSV or JSON; amounts in Indian digit grouping.
Install
cd taxlots
uv venv .venv
uv pip install -p .venv/bin/python -e '.[dev]'
source .venv/bin/activate
Requires Python 3.12+. Runtime dependencies: click, rich.
Quickstart (with the bundled examples)
cd examples # contains taxlots.toml, which points at the files below
taxlots import --profile zerodha --account zerodha zerodha_tradebook_2016_2026.csv
taxlots import mf_and_other_accounts.csv
taxlots summary --fy 2024-25
taxlots gains --fy 2024-25
taxlots schedule112a --fy 2024-25 --out 112a.csv
taxlots holdings --as-of 2026-03-31
taxlots harvest --fy 2025-26 --target-ltcg 125000
taxlots explain 1012
The examples cover 2016–2026 across four accounts: pre-2018 holdings, an INFY bonus, HDFCBANK and IRCTC splits, the HDFC → HDFCBANK merger, debt funds bought before and after 1-Apr-2023, a gold ETF across all three gold regimes, a US share via LRS, a REIT, intraday trades, and sales on both sides of 23-Jul-2024.
Usage (real output)
import
$ taxlots import --profile zerodha --account zerodha zerodha_tradebook_2016_2026.csv
zerodha_tradebook_2016_2026.csv: 27 trades read, 27 new, 0 duplicates skipped
$ taxlots import mf_and_other_accounts.csv
mf_and_other_accounts.csv: 20 trades read, 20 new, 0 duplicates skipped
$ taxlots import mf_and_other_accounts.csv
mf_and_other_accounts.csv: 20 trades read, 0 new, 20 duplicates skipped
gains
Each delivery sale, broken down by the lots it consumed. --format csv|json adds
days_held, term, cost, fmv_2018, indexed_cost and account columns.
$ taxlots gains --fy 2024-25
Realized capital gains FY 2024-25
┏━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ sale_id ┃ sale_date ┃ symbol ┃ lot_id ┃ acquired ┃ qty ┃ section ┃ rate ┃ sale_value ┃ expenses ┃ cost_used ┃ gain ┃
┡━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ C-05 │ 2024-05-20 │ CORPBOND-A │ C-02 │ 2019-04-15 │ 1000 │ 112 │ 20% │ 28,400.00 │ 0.00 │ 27,005.19 │ 1,394.81 │
│ 1012 │ 2024-06-10 │ INFY │ 1002 │ 2016-06-15 │ 100 │ 112A │ 10% │ 1,50,000.00 │ 0.00 │ 1,18,000.00 │ 32,000.00 │
│ 1012 │ 2024-06-10 │ INFY │ BONUS-INE009A01021-2018-09-04 │ 2018-09-04 │ 50 │ 112A │ 10% │ 75,000.00 │ 0.00 │ 0.00 │ 75,000.00 │
│ 1013 │ 2024-07-01 │ TATAMOTORS │ 1010 │ 2024-01-05 │ 200 │ 111A │ 15% │ 1,96,000.00 │ 0.00 │ 1,80,000.00 │ 16,000.00 │
│ 1016 │ 2024-08-14 │ IRCTC │ 1006 │ 2020-03-02 │ 60 │ 112A │ 12.5% │ 57,000.00 │ 0.00 │ 21,600.00 │ 35,400.00 │
│ 1017 │ 2024-09-02 │ RELIANCE │ 1011 │ 2024-03-11 │ 50 │ 111A │ 20% │ 1,50,000.00 │ 0.00 │ 1,45,000.00 │ 5,000.00 │
│ C-06 │ 2024-09-15 │ PPFAS-FLEXI │ C-01 │ 2017-11-10 │ 300 │ 112A │ 12.5% │ 23,400.00 │ 0.00 │ 7,050.00 │ 16,350.00 │
│ 1018 │ 2024-10-01 │ EMBASSY │ 1009 │ 2022-02-01 │ 100 │ 112A │ 12.5% │ 37,000.00 │ 0.00 │ 38,000.00 │ -1,000.00 │
│ C-07 │ 2024-11-11 │ CORPBOND-A │ C-03 │ 2021-12-01 │ 500 │ 112 │ 12.5% │ 14,500.00 │ 0.00 │ 12,500.00 │ 2,000.00 │
│ 1021 │ 2024-11-20 │ TATAMOTORS │ 1015 │ 2024-08-05 │ 100 │ 111A │ 20% │ 80,000.00 │ 0.00 │ 1,10,000.00 │ -30,000.00 │
│ 1022 │ 2024-12-10 │ SBIN │ 1001 │ 2016-03-01 │ 100 │ 112A │ 12.5% │ 79,000.00 │ 0.00 │ 31,000.00 │ 48,000.00 │
│ 1023 │ 2025-01-20 │ HDFCBANK │ 1003 │ 2017-03-10 │ 100 │ 112A │ 12.5% │ 1,65,000.00 │ 0.00 │ 99,500.00 │ 65,500.00 │
│ G-05 │ 2025-02-10 │ GOLDBEES │ G-02 │ 2023-05-15 │ 500 │ 50AA │ slab │ 31,000.00 │ 0.00 │ 24,000.00 │ 7,000.00 │
│ I-02 │ 2025-03-05 │ HDFCBANK │ I-01 │ 2016-08-01 │ 42 │ 112A │ 12.5% │ 71,400.00 │ 71.40 │ 50,050.00 │ 21,278.60 │
└─────────┴────────────┴─────────────┴───────────────────────────────┴────────────┴──────┴─────────┴───────┴─────────────┴──────────┴─────────────┴────────────┘
Intraday (speculative business income, not capital gains)
┏━━━━━━━━━━━━┳━━━━━━━━┳━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
┃ date ┃ symbol ┃ qty ┃ buy_value ┃ sell_value ┃ charges ┃ pnl ┃
┡━━━━━━━━━━━━╇━━━━━━━━╇━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
│ 2024-11-05 │ TCS │ 10 │ 42,000.00 │ 42,300.00 │ 0.00 │ 300.00 │
└────────────┴────────┴─────┴───────────┴────────────┴─────────┴────────┘
summary
$ taxlots summary --fy 2024-25
Capital gains summary FY 2024-25
┏━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┓
┃ Bucket ┃ Term ┃ Gains ┃ Losses ┃ Net ┃ Set-off ┃ Exempt ┃ Taxable ┃ Rate ┃ Tax ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━┩
│ 111A @15% │ ST │ 16,000.00 │ 0.00 │ 16,000.00 │ 16,000.00 │ 0.00 │ 0.00 │ 15% │ 0.00 │
│ 111A @20% │ ST │ 5,000.00 │ -30,000.00 │ -25,000.00 │ 0.00 │ 0.00 │ 0.00 │ 20% │ 0.00 │
│ 50AA STCG (slab) │ ST │ 7,000.00 │ 0.00 │ 7,000.00 │ 7,000.00 │ 0.00 │ 0.00 │ 30% │ 0.00 │
│ 112A @10% │ LT │ 1,07,000.00 │ 0.00 │ 1,07,000.00 │ 0.00 │ 0.00 │ 1,07,000.00 │ 10% │ 10,700.00 │
│ 112A @12.5% │ LT │ 1,86,528.60 │ -1,000.00 │ 1,85,528.60 │ 605.19 │ 1,25,000.00 │ 59,923.41 │ 12.5% │ 7,490.43 │
│ 112 @12.5% │ LT │ 2,000.00 │ 0.00 │ 2,000.00 │ 0.00 │ 0.00 │ 2,000.00 │ 12.5% │ 250.00 │
│ 112 @20% indexed │ LT │ 1,394.81 │ 0.00 │ 1,394.81 │ 1,394.81 │ 0.00 │ 0.00 │ 20% │ 0.00 │
└──────────────────┴──────┴─────────────┴────────────┴─────────────┴───────────┴─────────────┴─────────────┴───────┴───────────┘
set-off: current-year STCL 7,000.00 set off against 50AA STCG (slab)
set-off: current-year STCL 1,394.81 set off against 112 @20% indexed
set-off: current-year STCL 16,000.00 set off against 111A @15%
set-off: current-year STCL 605.19 set off against 112A @12.5%
112A exemption: ₹1,25,000.00 used of ₹1,25,000.00
Speculative (intraday) income: ₹300.00 -> tax ₹90.00 at 30% slab
Tax ₹18,530.43 + surcharge ₹0.00 (0%) + cess 4% ₹741.22 = ₹19,271.64
note: FY 2024-25: ₹1,25,000 112A exemption applied for the whole year (Finance (No.2) Act 2024), allocated to 12.5% gains first.
Options: --slab-rate 30, --surcharge 15, --bf-stcl AMOUNT, --bf-ltcl AMOUNT, --format json.
holdings
unrealized is on a tax basis: the gain if sold at price on the as-of date, with grandfathering applied.
$ taxlots holdings --as-of 2026-03-31
Open lots as of 2026-03-31
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
┃ lot_id ┃ account ┃ symbol ┃ acquired ┃ qty ┃ cost ┃ price ┃ value ┃ unrealized ┃ days_held ┃ becomes_ltcg_on ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
│ G-07 │ coin │ GOLDBEES │ 2025-10-01 │ 300 │ 29,400.00 │ 96.00 │ 28,800.00 │ -600.00 │ 181 │ 2026-10-02 │
│ BONUS-INE009A01021-2018-09-04 │ zerodha │ INFY │ 2018-09-04 │ 50 │ 0.00 │ 1,550.00 │ 77,500.00 │ 77,500.00 │ 2765 │ already │
│ 1006 │ zerodha │ IRCTC │ 2020-03-02 │ 40 │ 14,400.00 │ 780.00 │ 31,200.00 │ 16,800.00 │ 2220 │ already │
│ 1007 │ zerodha │ ITC │ 2020-06-15 │ 50 │ 10,000.00 │ 410.00 │ 20,500.00 │ 10,500.00 │ 2115 │ already │
│ 1008 │ zerodha │ ITC │ 2021-09-01 │ 50 │ 11,000.00 │ 410.00 │ 20,500.00 │ 9,500.00 │ 1672 │ already │
│ C-09 │ coin │ LIQUID-B │ 2024-02-01 │ 1000 │ 10,400.00 │ 12.10 │ 12,100.00 │ 1,700.00 │ 789 │ never (sec 50AA) │
│ C-01 │ coin │ PPFAS-FLEXI │ 2017-11-10 │ 200 │ 4,400.00 │ 88.00 │ 17,600.00 │ 12,900.00 │ 3063 │ already │
│ 1014 │ zerodha │ SBIN │ 2024-08-01 │ 100 │ 84,000.00 │ 760.00 │ 76,000.00 │ -8,000.00 │ 607 │ already │
└───────────────────────────────┴─────────┴─────────────┴────────────┴──────┴───────────┴──────────┴───────────┴────────────┴───────────┴──────────────────┘
harvest
$ taxlots harvest --fy 2025-26 --target-ltcg 125000
Harvest plan for FY 2025-26 as of 2026-03-31: target ₹1,25,000.00, already realised 112A ₹23,000.00
┏━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Account ┃ Symbol ┃ ISIN ┃ Sell qty ┃ Price ┃ LTCG ┃ Lots (FIFO) ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ zerodha │ INFY │ INE009A01021 │ 50 │ 1,550.00 │ 77,500.00 │ BONUS-INE009A01021-2018-09-04:50 │
│ zerodha │ IRCTC │ INE335Y01020 │ 40 │ 780.00 │ 16,800.00 │ 1006:40 │
│ zerodha │ ITC │ INE154A01025 │ 36 │ 410.00 │ 7,560.00 │ 1007:36 │
│ coin │ PPFAS-FLEXI │ INF879O01027 │ 2.17 │ 88.00 │ 139.97 │ C-01:2.17 │
└─────────┴─────────────┴──────────────┴──────────┴──────────┴───────────┴──────────────────────────────────┘
Harvested ₹1,01,999.97; unused ₹0.04. Re-buy after selling to reset cost basis (no wash-sale rule in India).
explain
$ taxlots explain 1012
Sale 1012: 150 x INFY (INE009A01021, listed_equity) on 2024-06-10 @ ₹1,500.00, account 'zerodha'
FY 2024-25; regime: pre-23-Jul-2024 rates
Sale value ₹2,25,000.00, sell charges ₹0.00 allocated pro-rata by quantity as transfer expenses (sec 48(i))
Lots matched by FIFO (sec 45(2A) Explanation):
[1] lot 1002 (buy) acquired 2016-06-15, 100 units, held 2917 days
history: 31-Jan-2018 FMV stamped at 1180/unit
Holding-period threshold: more than 12 months (sec 2(42A)); long-term from 2017-06-16 -> LONG-term
Sale value 1,50,000.00 | expenses 0.00 | actual cost incl. buy charges 95,000.00
Grandfathering (sec 55(2)(ac)): FMV 31-Jan-2018 1,180.00/unit = 1,18,000.00; max(cost 95,000.00, min(FMV 1,18,000.00, sale 1,50,000.00)) = 1,18,000.00
Gain = 1,50,000.00 - 0.00 - 1,18,000.00 = 32,000.00
Taxed under sec 112A at 10%
[2] lot BONUS-INE009A01021-2018-09-04 (bonus) acquired 2018-09-04, 50 units, held 2106 days
history: 2018-09-04 bonus 1:1 on 100 held; cost 0 (sec 55(2)(aa))
Holding-period threshold: more than 12 months (sec 2(42A)); long-term from 2019-09-05 -> LONG-term
Sale value 75,000.00 | expenses 0.00 | actual cost incl. buy charges 0.00
Gain = 75,000.00 - 0.00 - 0.00 = 75,000.00
Taxed under sec 112A at 10%
Total gain on this sale: ₹1,07,000.00
schedule112a
Columns follow ITR-2/ITR-3 Schedule 112A, with an extra "Transferred" column (before / on-or-after 23-Jul-2024) for the FY 2024-25 split. Grandfathered rows are listed scrip-wise.
$ taxlots schedule112a --fy 2024-25
Share/Unit acquired,Transferred,ISIN Code,Name of the Share/Unit,No. of Shares/Units,Sale-price per Share/Unit,Full Value of Consideration (4*5),Cost of acquisition without indexation (higher of 8 and 9),Cost of acquisition,"If acquired before 01.02.2018, lower of 6 and 11",FMV per share/unit as on 31.01.2018,Total FMV as per section 55(2)(ac) (4*10),Expenditure wholly and exclusively in connection with transfer,Total deductions (7+12),Balance (6-13)
On or before 31.01.2018,Before 23.07.2024,INE009A01021,Infosys Ltd,100,1500.00,150000.00,118000.00,95000.00,118000.00,1180.00,118000.00,0.00,118000.00,32000.00
After 31.01.2018,Before 23.07.2024,INE009A01021,Infosys Ltd,50,1500.00,75000.00,0.00,0.00,,,,0.00,0.00,75000.00
After 31.01.2018,On or after 23.07.2024,INE335Y01020,Indian Railway Catering & Tourism Corp,60,950.00,57000.00,21600.00,21600.00,,,,0.00,21600.00,35400.00
On or before 31.01.2018,On or after 23.07.2024,INF879O01027,Parag Parikh Flexi Cap Fund - Direct Growth,300,78.00,23400.00,7050.00,6600.00,7050.00,23.50,7050.00,0.00,7050.00,16350.00
After 31.01.2018,On or after 23.07.2024,INE041025011,Embassy Office Parks REIT,100,370.00,37000.00,38000.00,38000.00,,,,0.00,38000.00,-1000.00
On or before 31.01.2018,On or after 23.07.2024,INE062A01020,State Bank of India,100,790.00,79000.00,31000.00,16000.00,31000.00,310.00,31000.00,0.00,31000.00,48000.00
On or before 31.01.2018,On or after 23.07.2024,INE040A01034,HDFC Bank Ltd,100,1650.00,165000.00,99500.00,60000.00,99500.00,995.00,99500.00,0.00,99500.00,65500.00
On or before 31.01.2018,On or after 23.07.2024,INE040A01034,HDFC Bank Ltd,42,1700.00,71400.00,50050.00,50050.00,46500.00,1107.14,46500.00,71.40,50121.40,21278.60
Configuration
taxlots.toml is read from the current directory, or from --config PATH. Paths inside it are
relative to the file:
db = "taxlots.db"
assets = "assets.toml" # or assets.csv: isin,symbol,name,asset_type,fmv_2018
corporate_actions = "corporate_actions.toml"
prices = "prices.csv" # isin,symbol,price (for holdings/harvest)
slab_rate = 30
[profiles]
groww = "profiles/groww.toml" # custom importer profiles by name
[matching]
non_demat_accounts = ["folio"]
[[specific_id]] # only for non-demat accounts
sale = "S-123"
lots = [{ buy = "B-7", qty = 10 }]
Global flags --db, --assets and --actions override the config.
Asset types: listed_equity, equity_mf, debt_mf, foreign_equity, gold_etf,
reit_invit. Equity ISINs (INE…) missing from the master default to listed_equity with a
warning. Any other unknown ISIN is an error, because guessing a fund's tax type is unsafe.
Corporate actions (date = allotment/effective date):
[[split]] # 1 old share -> 5 new
isin = "INE335Y01020"
date = 2021-10-28
old = 1
new = 5
[[bonus]] # 1 bonus share for every 1 held
isin = "INE009A01021"
date = 2018-09-04
bonus = 1
held = 1
[[merger]] # also used for symbol/ISIN renames (old = new = 1)
old_isin = "INE001A01036"
new_isin = "INE040A01034"
new_symbol = "HDFCBANK"
date = 2023-07-13
old = 25
new = 42
Import profiles: built-in zerodha and generic
(date,symbol,isin,exchange,side,qty,price[,charges][,account][,trade_id]). See
docs/design.md for the profile format.
Architecture
| Module | Role |
|---|---|
rules.py |
Tax law as pure functions: FY, CII, holding periods, section/rate by sale date, exemption |
engine.py |
Intraday split, corporate-action replay, FIFO/specific-ID matching, per-lot evaluation |
summary.py |
Bucketing, loss set-off, 112A exemption, carry-forward, tax estimate |
harvest.py |
FIFO-prefix greedy harvesting planner |
reports.py |
Gains/holdings/112A rows, explain, Indian number formatting |
importer.py, store.py, master.py |
CSV profiles, SQLite trade store, master/config loaders |
cli.py |
Click commands (thin) |
docs/rules.md cites the relevant section for every rule and walks through the hand calculations that the tests assert. docs/design.md explains the event replay, FMV stamping, the set-off optimality argument and the harvest greedy.
Tests
.venv/bin/pytest -q
There are 61 tests: each rule with hand-computed numbers, FIFO across partial lots, every corporate
action, charge allocation, FY boundaries, the 23-Jul-2024 switch, set-off, surcharge, the
harvest optimiser and end-to-end CLI runs over examples/.
Limitations
- Not tax advice. Check the output against your broker's P&L and the ITR utility.
- Out of scope: land and buildings, unlisted Indian shares, bonds, exemptions under secs 54/54EC/54F, non-resident rules, the 87A rebate and using the basic exemption limit against 111A/112A gains.
- Put brokerage, GST, exchange fees and stamp duty in
charges, but not STT (it is not deductible). - Foreign-share prices must already be converted to INR.
- The FMV values in
examples/assets.tomlare illustrative. - Dividend/bonus stripping (sec 94(7)/(8)) and cash for fractional entitlements are not handled.
- Indexation for assets bought before 1-Apr-2001 is not supported.
License
MIT © Akshay Prabhu