nx_fin_taxlot.nx source
↩ module page · 57 lines · 2931 B
1// nx_fin_taxlot.nx -- R7 (Nishi Accounting): tax-lot accounting = the machinery to PARK gains legally. Exact
2// integer date math (Hinnant's civil-days algorithm) for holding periods, short/long-term classification (the
3// difference between ordinary-income and the lower long-term cap-gains rate), realized gain/loss, WASH-SALE
4// detection (a loss re-bought within +/-30 days is DISALLOWED), and the tax saved by harvesting a real loss.
5// Pure, i64 (cents/bps/days), NO floats. AI drafts + models; a licensed CPA signs the return. Pairs with the
6// additive nx_fin_ledger. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8const TL_MAGIC_146097: i64 = 146097
9const TL_MAGIC_719468: i64 = 719468
10const TL_MAGIC_10000: i64 = 10000
11
12const TL_LONGTERM_DAYS: i64 = 365 // held MORE than this (>1 year) = long-term
13const TL_WASH_WINDOW: i64 = 30 // +/- 30 days = the wash-sale window
14
15// serial day number (days from 1970-01-01) for a civil date -- exact, integer (Howard Hinnant's algorithm).
16func tl_days_from_civil(y0: i64, m: i64, d: i64) -> i64 {
17 var y: i64 = y0
18 if m <= 2 { y = y - 1 }
19 var era: i64 = y
20 if y < 0 { era = y - 399 }
21 era = era / 400
22 let yoe: i64 = y - era*400
23 var mm: i64 = m + 9
24 if m > 2 { mm = m - 3 }
25 let doy: i64 = (153*mm + 2)/5 + d - 1
26 let doe: i64 = yoe*365 + yoe/4 - yoe/100 + doy
27 return era*TL_MAGIC_146097 + doe - TL_MAGIC_719468
28}
29
30// days between two YYYYMMDD date keys (d2 - d1).
31func tl_days_between(d1_key: i64, d2_key: i64) -> i64 {
32 let y1: i64 = d1_key/TL_MAGIC_10000; let m1: i64 = (d1_key/100)%100; let dd1: i64 = d1_key%100
33 let y2: i64 = d2_key/TL_MAGIC_10000; let m2: i64 = (d2_key/100)%100; let dd2: i64 = d2_key%100
34 return tl_days_from_civil(y2, m2, dd2) - tl_days_from_civil(y1, m1, dd1)
35}
36
37// long-term iff held MORE than 365 days -> the lower long-term cap-gains rate.
38func tl_is_longterm(holding_days: i64) -> i64 { if holding_days > TL_LONGTERM_DAYS { return 1 } return 0 }
39
40// realized gain/loss in cents = (sale_px - cost_px) * shares (per-share prices in cents).
41func tl_realized_cents(sale_px_cents: i64, cost_px_cents: i64, shares: i64) -> i64 { return (sale_px_cents - cost_px_cents) * shares }
42
43// wash sale: sold at a LOSS and re-bought a substantially identical security within +/-30 days -> loss DISALLOWED
44// (deferred into the new lot's basis). Returns 1 if the loss is a wash.
45func tl_is_wash(realized_cents: i64, days_to_repurchase: i64) -> i64 {
46 if realized_cents >= 0 { return 0 }
47 var ad: i64 = days_to_repurchase; if ad < 0 { ad = 0 - ad }
48 if ad <= TL_WASH_WINDOW { return 1 }
49 return 0
50}
51
52// tax saved by harvesting a (non-wash) loss = |loss| * tax_rate_bps. A gain harvests nothing -> 0.
53func tl_harvest_benefit(realized_cents: i64, tax_rate_bps: i64) -> i64 {
54 if realized_cents >= 0 { return 0 }
55 let loss: i64 = 0 - realized_cents
56 return loss * tax_rate_bps / TL_MAGIC_10000
57}