CLI · commit 17aace5

fdladder

Fixed deposit & bond ladder builder with tax and liquidity planning

Download fdladder.zip
Quickstart
unzip fdladder.zip && cd fdladder
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/python -m pytest -q

fdladder

Fixed deposit and bond ladder builder for conservative savers in India. Give it an amount, an investor profile, a liquidity schedule and a rate card, and it designs a ladder across bank FDs, small finance bank FDs, corporate FDs, T-bills, G-secs, RBI floating rate bonds and tax-free bonds. The ladder meets every planned withdrawal, respects per-bank caps and the DICGC Rs 5 lakh limit, and maximises post-tax value. It also tracks existing deposits: maturity calendar, FY-wise interest and TDS statements for ITR and Form 26AS reconciliation, and an iCal export.

Features

Install

uv venv .venv
uv pip install -p .venv/bin/python -e '.[dev]'

Python 3.12+. Runtime dependencies: click, rich, pyyaml, numpy, scipy.

Quickstart

.venv/bin/fdladder plan examples/plan.yaml --rates examples/rates.csv
.venv/bin/fdladder compare examples/plan.yaml --rates examples/rates.csv
.venv/bin/fdladder holdings examples/holdings.csv --fy 2026-27 --senior --slab 20
.venv/bin/fdladder calendar examples/holdings.csv --ical -o maturities.ics
.venv/bin/fdladder calc fd --amount 500000 --rate 7.25 --days 400 --senior

The rate card in examples/rates.csv contains illustrative numbers and fictional issuers. They are not live rates.

Usage

fdladder calc fd

Single-instrument calculator. --senior adds --senior-premium (default 0.50) to the rate and uses the senior TDS threshold and 80TTB.

$ fdladder calc fd --amount 100000 --rate 7.25 --days 400 --start 2026-01-15
┌──────────────────────────────────┬──────────────────────────────────────────────────────────────┐
│ Rate applied                     │ 7.25%                                                        │
│ Convention                       │ quarterly compounding, simple interest for the broken period │
│ Maturity value                   │ ₹108,196                                                     │
│ Interest                         │ ₹8,196                                                       │
│ Effective annual yield           │ 7.45%                                                        │
│ Premature close at day 200       │ ₹103,459 (rate - 1%)                                         │
│ Tax at slab (if all in one year) │ ₹2,557                                                       │
│ Post-tax yield                   │ 5.13%                                                        │
└──────────────────────────────────┴──────────────────────────────────────────────────────────────┘
Interest by financial year                            
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
┃ FY      ┃ Interest accrued ┃ TDS (this bank alone) ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━┩
│ 2025-26 │           ₹1,510 │                    ₹0 │
│ 2026-27 │           ₹6,687 │                    ₹0 │
└─────────┴──────────────────┴───────────────────────┘

Non-cumulative payout (--payout monthly|quarterly):

$ fdladder calc fd --amount 120000 --rate 7.2 --days 365 --payout monthly --start 2026-04-01
┌──────────────────────────────────┬─────────────────────────────────────────┐
│ Rate applied                     │ 7.20%                                   │
│ Convention                       │ monthly payout, interest not compounded │
│ Monthly payout                   │ ₹716                                    │
│ Total interest                   │ ₹8,589                                  │
│ Maturity proceeds                │ ₹120,716                                │
│ Tax at slab (if all in one year) │ ₹2,680                                  │
│ Post-tax yield                   │ 5.04%                                   │
└──────────────────────────────────┴─────────────────────────────────────────┘
Interest by financial year                            
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
┃ FY      ┃ Interest accrued ┃ TDS (this bank alone) ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━┩
│ 2026-27 │           ₹8,589 │                    ₹0 │
└─────────┴──────────────────┴───────────────────────┘

Other options: --slab, --cess, --regime, --no-pan, --form15, --other-income, --penalty.

fdladder calc tbill and calc gsec

$ fdladder calc tbill --days 91 --discount 6.25
┌──────────────────┬──────────────────────────────┐
│ Price            │ 98,441.7808 per 100,000 face │
│ Discount rate    │ 6.2500%                      │
│ Investment yield │ 6.3489%                      │
└──────────────────┴──────────────────────────────┘

$ fdladder calc gsec --coupon 7.26 --maturity 2033-08-22 --settle 2026-10-16 --price 101.25
┌──────────────────┬──────────┐
│ Clean price      │ 101.2500 │
│ Accrued interest │ 1.0890   │
│ Dirty price      │ 102.3390 │
│ YTM              │ 7.0245%  │
└──────────────────┴──────────┘

tbill takes exactly one of --price, --discount, --yield; gsec takes exactly one of --price, --ytm.

fdladder plan PLAN.yaml --rates RATES.csv

Builds an allocation (--strategy optimizer|ladder|both, default optimizer; --json for machine output) and prints the allocation, the liquidity check, FY-wise interest and tax, expected TDS per payer and the regime comparison. Plan file format: see examples/plan.yaml.

$ fdladder plan examples/plan.yaml --rates examples/rates.csv
Plan: ₹2,500,000 for 1825 days from 2026-04-01, senior citizen, old regime at 20% + 4% cess

Allocation (optimizer)                                                                                                       
┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Issuer                 ┃ Type               ┃ Tenor ┃  Rate ┃   Amount ┃ Matures    ┃     Maturity value ┃ Post-tax yield ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ Sample Public Bank A   │ bank_fd_cumulative │  183d │ 6.90% │  ₹23,000 │ 2026-10-01 │            ₹23,803 │          5.59% │
│ Sample                 │ bank_fd_cumulative │  183d │ 6.75% │ ₹112,000 │ 2026-10-01 │           ₹115,823 │          5.46% │
│ Cooperative-style Bank │                    │       │       │          │            │                    │                │
│ F                      │                    │       │       │          │            │                    │                │
│ Sample Public Bank A   │ bank_fd_cumulative │  440d │ 7.25% │ ₹176,000 │ 2027-06-15 │           ₹191,928 │          5.91% │
│ Sample Public Bank A   │ bank_fd_cumulative │  731d │ 7.10% │ ₹245,000 │ 2028-04-01 │           ₹282,085 │          5.82% │
│ Sample Small Finance   │ sfb_fd             │  731d │ 8.40% │   ₹2,000 │ 2028-04-01 │             ₹2,362 │          6.92% │
│ Bank D                 │                    │       │       │          │            │                    │                │
│ Sample Public Bank A   │ bank_fd_cumulative │ 1825d │ 7.00% │   ₹1,000 │ 2031-03-31 │             ₹1,415 │          5.85% │
│ Sample Private Bank B  │ bank_fd_cumulative │ 1825d │ 7.30% │ ₹348,000 │ 2031-03-31 │           ₹499,652 │          6.11% │
│ Sample Private Bank C  │ bank_fd_cumulative │ 1825d │ 7.25% │ ₹349,000 │ 2031-03-31 │           ₹499,859 │          6.07% │
│ Sample Small Finance   │ sfb_fd             │ 1825d │ 7.75% │ ₹339,000 │ 2031-03-31 │           ₹497,599 │          6.51% │
│ Bank D                 │                    │       │       │          │            │                    │                │
│ Sample Small Finance   │ sfb_fd             │ 1825d │ 8.10% │ ₹334,000 │ 2031-03-31 │           ₹498,745 │          6.82% │
│ Bank E                 │                    │       │       │          │            │                    │                │
│ RBI Floating Rate Bond │ rbi_frb            │ 1825d │ 7.45% │ ₹391,000 │ 2031-03-31 │ ₹391,000 + payouts │          5.99% │
│ Savings / liquid       │ liquid             │     - │ 3.00% │ ₹180,000 │ on demand  │                  - │          2.38% │
│ reserve                │                    │       │       │          │            │                    │                │
└────────────────────────┴────────────────────┴───────┴───────┴──────────┴────────────┴────────────────────┴────────────────┘
Liquidity check (post-tax cash vs          
withdrawals + reserve)                     
┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━┓
┃ Date       ┃   Needed ┃ Available ┃ OK  ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━┩
│ 2026-04-01 │ ₹180,000 │  ₹180,000 │ yes │
│ 2026-10-01 │ ₹330,000 │  ₹330,198 │ yes │
│ 2027-06-15 │ ₹530,000 │  ₹530,349 │ yes │
│ 2028-04-01 │ ₹830,000 │  ₹830,078 │ yes │
└────────────┴──────────┴───────────┴─────┘
Financial-year interest and tax                                                 
┏━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━┓
┃ FY      ┃ Interest ┃ Tax-free ┃   80TTB ┃ Tax at slab ┃    TDS ┃ Net payable ┃
┡━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━┩
│ 2026-27 │ ₹172,014 │       ₹0 │ ₹50,000 │     ₹25,379 │ ₹2,913 │     ₹22,466 │
│ 2027-28 │ ₹167,266 │       ₹0 │ ₹50,000 │     ₹24,391 │ ₹2,921 │     ₹21,470 │
│ 2028-29 │ ₹153,699 │       ₹0 │ ₹50,000 │     ₹21,569 │ ₹2,913 │     ₹18,656 │
│ 2029-30 │ ₹163,456 │       ₹0 │ ₹50,000 │     ₹23,599 │ ₹2,913 │     ₹20,686 │
│ 2030-31 │ ₹173,483 │       ₹0 │ ₹50,000 │     ₹25,684 │ ₹2,905 │     ₹22,779 │
└─────────┴──────────┴──────────┴─────────┴─────────────┴────────┴─────────────┘
TDS expected (for Form 26AS reconciliation)                         
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┓
┃ FY      ┃ Payer                  ┃ Interest ┃ Threshold ┃    TDS ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━┩
│ 2026-27 │ RBI Floating Rate Bond │  ₹29,130 │   ₹10,000 │ ₹2,913 │
│ 2027-28 │ RBI Floating Rate Bond │  ₹29,209 │   ₹10,000 │ ₹2,921 │
│ 2028-29 │ RBI Floating Rate Bond │  ₹29,129 │   ₹10,000 │ ₹2,913 │
│ 2029-30 │ RBI Floating Rate Bond │  ₹29,130 │   ₹10,000 │ ₹2,913 │
│ 2030-31 │ RBI Floating Rate Bond │  ₹29,050 │   ₹10,000 │ ₹2,905 │
└─────────┴────────────────────────┴──────────┴───────────┴────────┘
Post-tax value at horizon (2031-03-31): ₹2,532,021  post-tax IRR: 5.91%
Tax on this plan over the horizon by regime: old ₹120,623 (slab 20%), new ₹86,311 (slab 10%)

fdladder compare PLAN.yaml --rates RATES.csv

$ fdladder compare examples/plan.yaml --rates examples/rates.csv
Post-tax value at horizon and IRR (₹2,500,000, 1825 days; scenarios shift reinvestment rates by -1 / +1 point)        
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Strategy       ┃ flat: value ┃   IRR ┃ falling: value ┃   IRR ┃ rising: value ┃   IRR ┃ Shortfall ┃ Early closures ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ single long FD │  ₹2,488,909 │ 5.58% │     ₹2,488,909 │ 5.58% │    ₹2,488,909 │ 5.58% │        ₹0 │              2 │
│ ladder         │  ₹2,458,399 │ 5.34% │     ₹2,449,420 │ 5.27% │    ₹2,467,469 │ 5.41% │        ₹0 │              0 │
│ optimizer      │  ₹2,533,020 │ 5.92% │     ₹2,532,847 │ 5.92% │    ₹2,533,195 │ 5.92% │        ₹0 │              0 │
│ debt fund      │  ₹2,502,498 │ 5.68% │     ₹2,397,708 │ 4.85% │    ₹2,611,470 │ 6.51% │        ₹0 │              0 │
└────────────────┴─────────────┴───────┴────────────────┴───────┴───────────────┴───────┴───────────┴────────────────┘
Shortfall and early closures are shown for the flat scenario.

The single long FD has to break deposits early for the withdrawals (see the early closure count); the debt fund wins when rates rise and loses when they fall; the optimizer's liquidity-matched ladder is the least sensitive to the scenario because its reinvestment is small. See docs/optimizer.md for how the simulation works.

fdladder holdings HOLDINGS.csv --fy 2026-27

Interest accrued in the FY per deposit, TDS per payer, tax at slab, and an alert list of maturities within --within days (default 60). Tax options: --senior --slab 20 --cess 4 --regime old|new --no-pan --form15 --other-income N. --asof fixes the alert date.

$ fdladder holdings examples/holdings.csv --fy 2026-27 --senior --slab 20 --asof 2026-10-16 --within 90
Holdings interest, FY 2026-27                                                                                                
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Issuer                             ┃ Type                  ┃ Principal ┃  Rate ┃ Start      ┃ Maturity   ┃ Interest in FY ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ Sample Public Bank A               │ bank_fd_cumulative    │  ₹500,000 │ 7.25% │ 2025-04-10 │ 2026-11-20 │        ₹25,186 │
│ Sample Public Bank A               │ bank_fd_cumulative    │  ₹300,000 │ 7.00% │ 2025-09-01 │ 2027-09-01 │        ₹22,445 │
│ Sample Private Bank B              │ bank_fd_cumulative    │  ₹400,000 │ 7.60% │ 2025-06-15 │ 2026-12-15 │        ₹23,200 │
│ Sample Private Bank B              │ bank_fd_noncumulative │  ₹200,000 │ 6.90% │ 2025-10-05 │ 2028-10-05 │        ₹13,721 │
│ Sample Small Finance Bank D        │ sfb_fd                │  ₹450,000 │ 8.60% │ 2025-12-01 │ 2027-02-01 │        ₹34,226 │
│ Sample Finance Co (AAA)            │ corporate_fd          │  ₹300,000 │ 7.90% │ 2025-08-01 │ 2028-08-01 │        ₹25,718 │
│ Government of India (T-bill)       │ tbill                 │  ₹200,000 │ 6.50% │ 2026-08-01 │ 2026-10-31 │         ₹3,241 │
│ Sample PSU Tax-free Bond           │ taxfree_bond          │  ₹500,000 │ 5.60% │ 2024-03-01 │ 2029-03-01 │        ₹28,000 │
│ (secondary)                        │                       │           │       │            │            │                │
└────────────────────────────────────┴───────────────────────┴───────────┴───────┴────────────┴────────────┴────────────────┘
TDS statement, FY 2026-27 (reconcile with Form 26AS)                                            
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┓
┃ Payer                                ┃ Type                  ┃ Interest ┃ Threshold ┃    TDS ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━┩
│ Government of India (T-bill)         │ tbill                 │   ₹3,241 │    no TDS │     ₹0 │
│ Sample Finance Co (AAA)              │ corporate_fd          │  ₹25,718 │    ₹5,000 │ ₹2,572 │
│ Sample PSU Tax-free Bond (secondary) │ taxfree_bond          │  ₹28,000 │    no TDS │     ₹0 │
│ Sample Private Bank B                │ bank_fd_cumulative    │  ₹23,200 │   ₹50,000 │     ₹0 │
│ Sample Private Bank B                │ bank_fd_noncumulative │  ₹13,721 │   ₹50,000 │     ₹0 │
│ Sample Public Bank A                 │ bank_fd_cumulative    │  ₹47,631 │   ₹50,000 │     ₹0 │
│ Sample Small Finance Bank D          │ sfb_fd                │  ₹34,226 │   ₹50,000 │     ₹0 │
└──────────────────────────────────────┴───────────────────────┴──────────┴───────────┴────────┘
Total interest ₹175,738 (tax-free ₹28,000), 80TTB deduction ₹50,000, tax at slab ₹20,329, TDS ₹2,572, net payable ₹17,758
hint: total income looks within the nil-tax limit; Form 15H can stop TDS next year.
Maturities from 2026-10-16 within 90 days                                                              
┏━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Date       ┃  In ┃ Issuer                       ┃ Type               ┃ Proceeds ┃ Account           ┃
┡━━━━━━━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ 2026-10-31 │ 15d │ Government of India (T-bill) │ tbill              │ ₹203,241 │ Retail Direct     │
│ 2026-11-20 │ 35d │ Sample Public Bank A         │ bank_fd_cumulative │ ₹561,490 │ joint with spouse │
│ 2026-12-15 │ 60d │ Sample Private Bank B        │ bank_fd_cumulative │ ₹447,868 │ self              │
└────────────┴─────┴──────────────────────────────┴────────────────────┴──────────┴───────────────────┘

fdladder calendar HOLDINGS.csv

Maturity table (--within N, --asof DATE), or an iCalendar file with --ical (stdout, or -o file.ics; --alarm-days sets the reminder lead time, default 7).

$ fdladder calendar examples/holdings.csv --ical --asof 2026-10-16 --within 40
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//fdladder//maturities//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:GovernmentofIndia(T-bill)-tbill-2026-08-01-200000-2026-10-31@fdladder
DTSTAMP:20261016T000000Z
DTSTART;VALUE=DATE:20261031
SUMMARY:Government of India (T-bill) tbill matures: Rs 203\,241
DESCRIPTION:Principal 200\,000 at 6.50% from 2026-08-01. Maturity proceeds 
 203\,241. Retail Direct
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Government of India (T-bill) tbill matures: Rs 203\,241
TRIGGER:-P7D
END:VALARM
END:VEVENT
BEGIN:VEVENT
UID:SamplePublicBankA-bank_fd_cumulative-2025-04-10-500000-2026-11-20@fdlad
 der
DTSTAMP:20261016T000000Z
DTSTART;VALUE=DATE:20261120
...

Input files

Plan YAML (examples/plan.yaml): amount, start_date, horizon_years or horizon_days; investor (age, slab_rate as a number or {old, new}, regime, other_income, has_pan, form15, dicgc_capacities, cess_pct); liquidity (withdrawals list of date/amount/note, emergency_reserve_months, monthly_expenses); preferences (max_per_bank, max_per_bank_pct, only_dicgc_insured, min_rating, idle_rate, liquidity_weight, ladder_rungs, lot).

Rate card CSV (examples/rates.csv, lines starting with # are ignored): issuer, type, tenor_min_days, tenor_max_days, rate, senior_premium, compounding, penalty_pct, rating, min_amount. One row per tenor band. type is one of bank_fd_cumulative, bank_fd_noncumulative, sfb_fd, corporate_fd, tbill, gsec, rbi_frb, taxfree_bond. compounding is quarterly (cumulative), payout_monthly, payout_quarterly, half_yearly (coupon bonds) or discount (T-bills).

Holdings CSV (examples/holdings.csv): issuer, type, principal, rate, start_date, maturity_date, compounding, penalty_pct, account. Type and compounding may be blank (cumulative bank FD, quarterly).

Architecture

Module Role
instruments.py Pure maths: FD values, payouts, T-bill, bond price/YTM, xirr
models.py Instrument, Position, Plan, TaxProfile, Allocation and their cash flows
taxes.py FY accrual, TDS thresholds, Form 15G/H, 80TTB, FY statements
ratecard.py, planio.py, tracker.py CSV/YAML loading and validation, candidate generation, maturity calendar
valuation.py Linear per-rupee coefficients shared by optimizer and ladder
optimizer.py, ladder.py MILP allocation and equal-rung generator
evaluate.py Post-tax value, liquidity check, exposures, FY statements for an allocation
simulate.py, planner.py Reinvestment simulation and strategy comparison
ical.py RFC 5545 export
cli.py Click commands and rich rendering

Deeper notes: docs/formulas.md, docs/tax-rules.md, docs/optimizer.md.

Tests

.venv/bin/pytest

100 tests covering hand-computed FD values (including broken periods), payout formulas, FY accrual splits, TDS thresholds per bank (senior, no PAN, Form 15), T-bill and YTM maths, property-based tests (hypothesis) that the optimizer satisfies liquidity, bank caps, DICGC limits and minimum amounts, the ladder generator, reinvestment scenarios, iCal output and the CLI end to end.

Limitations

See docs/optimizer.md for the complete list.