# 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

- Instrument maths with Indian bank conventions: simple interest below six months, quarterly compounding with exact broken periods, non-cumulative monthly/quarterly payouts, effective yield, premature withdrawal, T-bill discount/yield/price, G-sec YTM (Newton) with accrued interest.
- Tax engine: FY-wise accrual, TDS per bank (Rs 40,000 / Rs 50,000 senior, 20% without PAN, Form 15G/H), Section 80TTB, tax-free bonds, old vs new regime comparison.
- MILP optimizer (scipy/HiGHS) with liquidity constraints, per-bank caps, DICGC limits, minimum deposit sizes and minimum corporate rating; classic equal-rung ladder generator for comparison.
- Reinvestment simulation under flat, falling (-1 point) and rising (+1 point) rate scenarios: single long FD vs ladder vs optimizer vs debt fund.
- Holdings tracker with upcoming-maturity alerts and iCal export.

## Install

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

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

## Quickstart

```bash
.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/formulas.md), [`docs/tax-rules.md`](docs/tax-rules.md), [`docs/optimizer.md`](docs/optimizer.md).

## Tests

```bash
.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

- Rates are user input; the sample card is illustrative.
- Bank calculators differ from the 365/4-day quarter convention by a few rupees.
- The optimizer applies slab tax to interest; 80TTB and TDS timing appear in the statements only.
- Bonds are modelled as bought at par and held to maturity; premature closure of non-cumulative deposits and bonds is simplified.
- The RBI bond TDS threshold is an assumption. Verify tax rules before relying on any figure; this is a planning aid, not tax or investment advice.

See `docs/optimizer.md` for the complete list.
