Documentation
¶
Overview ¶
Package indianfinance implements zero-dependency finance formulas for India — loans, investments, GST, gratuity and income tax.
Every function here is extracted from the calculators running at https://emicalcs.com and is covered by the test suite in finance_test.go, which is a direct port of the JavaScript suite the same formulas ship with.
Statutory figures — tax slabs, gratuity ceilings, GST rates — change by notification. Where a function depends on one, the value is a documented OPTION with a default rather than a hidden constant, so you can update it yourself the day it moves without waiting for a release.
A note on types ¶
The JavaScript original takes months and years as numbers and rounds them. This port takes them as int where the underlying loop is integer-stepped, which removes the NaN and Infinity cases the JS version has to guard against at runtime. The tenure ceiling below is kept regardless.
MIT licensed.
Index ¶
- Constants
- func AnnualContributionMaturity(yearlyAmount, annualRatePct float64, years int) (float64, error)
- func CAGR(initial, final, years float64) float64
- func EMI(principal, annualRate float64, months int) float64
- func GSTInterest(cashTaxPaid float64, days int, annualRatePct float64) float64
- func Lumpsum(principal, annualRate, years float64) float64
- func SIPFutureValue(monthly, annualRate float64, months int) float64
- func TotalInterest(principal, annualRate float64, months int) float64
- type AmortRow
- type ErrTenure
- type GST
- type Gratuity
- type GratuityOptions
- type PrepaymentInput
- type PrepaymentResult
- type SWPInput
- type SWPResult
- type Slab
- type StepUpResult
- type Tax
- type TaxOptions
Constants ¶
const ( MaxMonths = 1200 MaxYears = MaxMonths / 12 )
Every schedule function below steps one period at a time, so the loop is bounded only by the tenure it is handed. Left unchecked, a bad tenure is an out-of-memory crash in the CALLER's process — not a slow answer — and there is no UI here to clamp the input first. 1200 months / 100 years is far beyond anything real (the longest home loan on offer runs 30-40 years), so the ceiling only ever fires on input that was never going to mean anything.
const DefaultGSTInterestRate = 18.0
DefaultGSTInterestRate is the notified annual rate under Section 50(1).
Variables ¶
This section is empty.
Functions ¶
func AnnualContributionMaturity ¶
AnnualContributionMaturity returns the maturity of an annual-contribution scheme such as PPF or SSY. It returns an *ErrTenure if years exceeds MaxYears.
func EMI ¶
EMI returns the reducing-balance monthly instalment. annualRate is a percentage, e.g. 8.5 for 8.5% p.a.
func GSTInterest ¶
GSTInterest returns interest on a late GST payment.
Under Rule 88B(1) interest runs on the tax actually paid in cash from the electronic cash ledger — NOT on the gross output liability. Getting this wrong overstates the interest, often by several times. Pass cashTaxPaid as the cash-ledger portion, not the gross bill.
Pass annualRatePct as DefaultGSTInterestRate unless the notified rate has moved. The two calculations side by side, cash-ledger against gross bill: https://emicalcs.com/gst-interest-calculator/
func SIPFutureValue ¶
SIPFutureValue returns the future value of a monthly SIP, with the contribution made at the start of each month.
func TotalInterest ¶
TotalInterest returns the total interest paid over the full tenure.
Types ¶
type AmortRow ¶
type AmortRow struct {
Month int
Opening float64
Interest float64
Principal float64
Closing float64
}
AmortRow is one month of an amortisation schedule.
type ErrTenure ¶
ErrTenure is returned when a tenure exceeds the ceiling. Use errors.Is to match it; the wrapped message carries the offending value.
type GST ¶
GST is a base amount, its tax and the gross total.
func GSTAdd ¶
GSTAdd adds GST to a base amount. Interactive version: https://emicalcs.com/gst-calculator/
type Gratuity ¶
type Gratuity struct {
Eligible bool
Amount float64
FormulaValue float64
Ceiling float64
Capped bool
}
Gratuity is the computed entitlement.
func ComputeGratuity ¶
func ComputeGratuity(lastSalary, years float64, opts GratuityOptions) Gratuity
ComputeGratuity applies the Code on Social Security, 2020 (in force 21 Nov 2025), whose text is published at https://labour.gov.in lastSalary is the last drawn monthly Basic + DA; years is completed years of service.
type GratuityOptions ¶
type GratuityOptions struct {
Ceiling float64 // 0 -> 20,00,000. Use 25,00,000 for Central Government civil employees.
MinYears float64 // 0 -> 5. Use 1 for fixed-term employees.
}
GratuityOptions carries the statutory figures that change by notification. The zero value means "use the defaults".
type PrepaymentInput ¶
type PrepaymentInput struct {
Principal float64
AnnualRate float64
Months int // original tenure
MonthlyExtra float64
LumpSum float64
LumpSumAtMonth int // 0 = before the first instalment
}
PrepaymentInput describes a prepayment scenario. The EMI is held fixed, so prepaying shortens the tenure rather than reducing the instalment.
type PrepaymentResult ¶
type PrepaymentResult struct {
InterestSaved float64
MonthsSaved int
NewMonths int
NewInterest float64
}
PrepaymentResult is what prepaying achieves.
func Prepayment ¶
func Prepayment(in PrepaymentInput) (PrepaymentResult, error)
Prepayment computes the effect of prepaying with the EMI held fixed, so the tenure shrinks rather than the instalment.
See the lever plotted against tenure at https://emicalcs.com/home-loan-prepayment-calculator/
It returns an *ErrTenure if Months exceeds MaxMonths.
type SWPInput ¶ added in v1.1.0
type SWPInput struct {
Corpus float64
MonthlyWithdrawal float64
AnnualRatePct float64
Years int
// AnnualIncreasePct raises the withdrawal on each anniversary, so its buying
// power holds against inflation. 0 is a flat SWP. Indian retirees typically
// use 5-6%.
AnnualIncreasePct float64
}
SWPInput describes a systematic withdrawal plan.
type SWPResult ¶ added in v1.1.0
type SWPResult struct {
FinalBalance float64
TotalWithdrawn float64
Gains float64
// DepletedAtMonth is the month the corpus ran dry, or 0 if it survived the
// full period.
DepletedAtMonth int
}
SWPResult is the outcome of a systematic withdrawal plan.
func SWP ¶ added in v1.1.0
SWP models a systematic withdrawal plan: money is taken out at the start of each month and the remainder stays invested, compounding monthly.
The variable that decides the outcome is AnnualIncreasePct, and most SWP calculators do not offer it. Worked example, from the test suite: a Rs 50,00,000 corpus withdrawing Rs 30,000 a month at 8% finishes 20 years with Rs 68,45,597 still in it — the corpus outgrows the withdrawals. Index that same Rs 30,000 to 6% inflation and it runs dry after 17 years 1 month. Same corpus, same return, same starting withdrawal, nearly three years of retirement gone.
Run your own numbers at https://emicalcs.com/swp-calculator/
It returns an *ErrTenure if Years exceeds MaxYears.
type Slab ¶
Slab is one income-tax bracket: everything up to Upto is taxed at Rate. The final slab must use math.Inf(1) as Upto.
func DefaultSlabs ¶
func DefaultSlabs() []Slab
DefaultSlabs are the new-regime slabs for FY 2026-27 (AY 2027-28), unchanged by Budget 2026.
type StepUpResult ¶
StepUpResult is the outcome of a step-up SIP.
func StepUpSIP ¶
func StepUpSIP(monthly, annualRate float64, years int, stepUpPct float64) (StepUpResult, error)
StepUpSIP models a SIP whose contribution rises by stepUpPct every 12 months.
Worth knowing: a step-up SIP does NOT beat a flat SIP of the same total outlay. It wins in headline terms only because more money goes in. Hold the money constant and the flat schedule wins, because its rupees compound for longer. See https://emicalcs.com/step-up-sip-calculator/
It returns an *ErrTenure if years exceeds MaxYears.
type Tax ¶
Tax is the computed liability.
func IncomeTaxNewRegime ¶
func IncomeTaxNewRegime(taxableIncome float64, opts TaxOptions) Tax
IncomeTaxNewRegime computes tax under Section 115BAC, including the Section 87A rebate with marginal relief — tax cannot exceed the income above the rebate threshold, otherwise earning one rupee more would cost more than one rupee. That relief band is easiest to see plotted: https://emicalcs.com/income-tax-calculator/
type TaxOptions ¶
type TaxOptions struct {
Slabs []Slab // nil -> DefaultSlabs
RebateUpto float64 // 0 -> 12,00,000 (Section 87A)
Cess float64 // 0 -> 0.04
}
TaxOptions overrides the statutory defaults. The zero value means "use the FY 2026-27 defaults".