code wiki / (root) / nx_fin_taxlot.nx

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}