Skip to content
bitzorcas
中EN

Reference

Industry Extensions Finance calculators

A deep guide to the bracket engine, comprehensive/labor/bonus tax, mortgage amortization, four-times-LPR lending rules and allocation, the 130% penalty heuristic, rounding, dates, and missing policy versioning.

Last updated

Finance is a set of static numeric functions, not a tax or credit decision system. It reuses formulas but has no taxpayer or contract aggregate, policy store, occurrence date, evidence, filing, approval, or result ledger.

1. BracketTaxCalculator

TaxBracket contains UpperLimit, Rate, and QuickDeduction. Despite its name, Auction and LegalCalculators also reuse it. CalcByQuickFormula chooses the first bracket whose upper limit contains the amount and otherwise uses the final bracket. CalcProgressive accumulates each interval.

Use a prevalidated progressive schedule
// Require ascending limits, nonnegative rates, and a final catch-all bracket first.
TaxBracket[] brackets =
[
new(100_000m, 0.02m, 0m),
new(decimal.MaxValue, 0.01m, 0m),
];
// The progressive engine accumulates but performs no validation or rounding.
var amount = BracketTaxCalculator.CalcProgressive(150_000m, brackets);
// 100000×2% + 50000×1% = 2500.
Debug.Assert(amount == 2_500m);

An empty schedule or nonpositive amount returns zero rather than Validation. Unordered, duplicate, negative-rate, negative-deduction, or incomplete schedules are accepted.

2. Comprehensive-income tax

The seven thresholds, rates, and quick deductions match the current official comprehensive-income table. Input is annual taxable income or annual income minus basic, social-insurance, and special-additional deductions. Final tax rounds two places AwayFromZero.

The second overload rejects only negative annual income. A negative deduction increases taxable income. There is no resident status, foreign income, other deduction, relief, prepaid tax, annual reconciliation, or TaxYear.

Do not treat the annual-income overload as a complete filing
// Model only the three deductions accepted by this convenience overload.
var result = IncomeTaxCalculator.CalcIndividualIncomeTax(
annualIncome: 300_000m,
basicDeduction: 60_000m,
socialInsuranceDeduction: 36_000m,
specialAdditionalDeduction: 24_000m);
// Success proves only the hard-coded seven-bracket formula ran.
if (result.IsFailure)
return result.Error;
return new TaxEstimate(result.Value, policyVersion, inputEvidenceHash);

TaxEstimate is a target wrapper DTO, not a current library type.

3. Labor-service withholding

Income up to 4,000 deducts 800; above 4,000 deducts 20%, followed by 20/30/40% quick-formula brackets. Official guidance also requires resident individuals to include labor remuneration in annual comprehensive-income reconciliation.

The caller must establish transaction/month aggregation, residency, withholding agent, and annual treatment. One decimal cannot represent these facts.

4. Annual bonus

The method divides bonus by 12 to pick a monthly bracket, then applies bonus × rate − quick deduction. The current official announcement allows residents to choose separate taxation or comprehensive-income inclusion through 2027-12-31.

The method has no TaxYear, cannot compare the two choices, and does not enforce one use per taxpayer per year. Without a policy EffectiveTo, it will continue calculating after expiry.

5. Equal-installment mortgage

Monthly rate is annualRate/12 and months is years×12. Decimal exponentiation computes (1+r)^n; zero interest uses principal/months.

The schedule rounds monthly interest and principal, absorbing residual principal in the final period. Total-interest API instead multiplies the already rounded monthly payment by months, so its result can differ by cents from summing schedule interest.

6. Equal-principal mortgage

Monthly principal is rounded two places, with the last period absorbing residual. Interest uses opening principal times monthly rate. The total-interest formula does not round each period and can differ from the schedule sum.

Reconcile from one schedule contract
// Fix principal, annual rate, and term; the API has no actual drawdown date.
var schedule = MortgageCalculator.CalcEqualPrincipalSchedule(
principal: 1_200_000m,
annualRate: 0.035m,
years: 20);
if (schedule.IsFailure)
return schedule.Error;
// A statement uses one per-period rounding rule and derives totals from its rows.
var totalInterest = schedule.Value.Sum(item => item.Interest);
var totalPrincipal = schedule.Value.Sum(item => item.Principal);
Debug.Assert(totalPrincipal == 1_200_000m);

There is no drawdown date, actual payment date, daily interest, leap-year rule, repricing, prepayment, fee, penalty interest, or bank-specific convention.

7. Mortgage capacity and overflow

Years has only a lower bound. years * 12 uses int arithmetic and can overflow; large month counts can overflow decimal exponentiation or allocate enormous schedules. Public wrappers need principal, rate, term, and maximum-row limits.

Principal zero produces zero values or an all-zero schedule. Annual rate has no reasonable upper bound, so a very high nonnegative rate can throw decimal overflow rather than return Result Failure.

8. Private-lending rate rule

ValidateInterestRateCeiling delegates to Domain InterestCalculator. GetInterestRateCeiling simply returns LPR×4 with no validation. The judicial interpretation ties the standard to the one-year LPR at contract formation and includes transitional treatment for older contracts.

The API does not accept contract date, filing/acceptance date, contract category, or whether the relationship is private lending. Policy selection must happen first.

9. Segmented interest

Each segment computes principal × annualRate × days / 365 with fixed principal. Negative rates fail; nonpositive days are silently skipped. Date subtraction excludes EndDate despite record comments describing it as inclusive.

Validate a timeline before the current segment engine
InterestSegment[] segments =
[
new(new DateTime(2026, 1, 1), new DateTime(2026, 2, 1), 0.04m),
new(new DateTime(2026, 2, 1), new DateTime(2026, 3, 1), 0.036m),
];
// Executable behavior is [StartDate, EndDate), so the boundary is not doubled.
var result = LoanDetailCalculator.CalcInterestBySegments(500_000m, segments);
// The engine does not reject overlap, gaps, or disorder.
if (result.IsFailure)
return result.Error;

Principal repayments during the period are not modeled and can make fixed-principal interest too high.

10. Allocation and compound ceiling

Allocation applies fees, then accrued interest, then principal; excess payment is Remaining. Each output component is rounded after allocation, so the displayed parts can differ slightly from the unrounded arithmetic.

Compound-ceiling validation computes principal×LPR×4×days/365 and compares a caller-supplied compound-interest amount. It does not derive or verify the interest ledger. This is arithmetic evidence, not a court determination.

11. PenaltyCalculator

Agreed, daily, and LPR-reference methods are direct formulas. SuggestAdjustedPenalty caps its suggestion at actualLoss×1.30 and returns zero when loss is zero.

The 2023 Civil Code contract interpretation says a penalty exceeding loss by 30% may generally be found excessive, while requiring assessment of parties, transaction, performance, fault, background, fairness, and good faith. A deterministic “adjusted amount” hides that discretion. A RiskIndicator would be a safer contract.

12. Numeric and error contract

Public APIs mix Result and bare decimal. Stable Finance codes coexist with Chinese descriptions and generic InvalidInput errors. Decimal overflow is not mapped to Result. Wrappers must bound input and provide one exception boundary.

Money has no Currency, rate has no Percent value object, and days have no DayCountConvention. Cross-currency and 360/365/actual conventions cannot be represented safely.

13. Test matrix

CapabilityRequired edges
BracketEvery bound ±0.01, disorder, negative rate, missing final bracket
Income taxSeven boundaries, negative deduction, tax year, official golden values
Labor800/4,000/20,000/50,000 boundaries and annual-reconciliation warning
BonusMonthly boundaries, separate/included comparison, 2027 expiry
MortgageZero rate, 1/30 years, row totals, cent differences, overflow/capacity
SegmentSame day, inclusivity, leap year, overlap/gap/order/repayment
AllocationZero, exact payoff, excess, rounding reconciliation
PenaltyZero loss, 130% boundary, unmodeled discretion factors

None of these tests currently exists.

14. Inspection commands

Terminal window
# Formula, rounding, date subtraction, and bracket behavior.
rg -n "Math.Round|AwayFromZero|TotalDays|CalcByQuickFormula|CalcProgressive|years \* 12" \
src/Platform/IndustryExtensions/BitzOrcas.Platform.IndustryExtensions.Finance -g '*.cs'
# Policy/date/currency/breakdown/test capabilities should have no complete implementation.
rg -n "TaxYear|ContractDate|Currency|DayCountConvention|PolicyVersion|\[Fact\]" \
src/Platform/IndustryExtensions tests -g '*.cs'

Industry Extensions overview · HR leave policies · Auction and GA

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%