daybook v0.4.0
1099-DA reconciliation, five high-volume global exchange adapters, and a shared CSV helpers module.
New features
1099-DA reconciliation
Starting with tax year 2025, US crypto exchanges are required to issue Form 1099-DA. daybook reconcile compares the IRS-reported numbers against daybook's own computation and flags every discrepancy.
daybook reconcile 2025 --1099da ~/Downloads/coinbase-1099da.csv- Matches each daybook disposal to a 1099-DA row by asset + date proximity + amount proximity (configurable tolerances).
- Flags daybook disposals missing from the 1099-DA, 1099-DA rows missing from daybook, and field-level mismatches (proceeds, cost basis, term, acquisition date).
- Recommends a Form 8949 checkbox (A / B / C) based on the result with a plain-text rationale.
- Output: human-readable text by default;
--format jsonfor machine consumption.
Per-disposal Form 8949 box assignment
Pass --1099da to daybook export --format 8949 and each disposal gets its own box based on the reconciliation:
daybook export 2025 --format 8949 --1099da coinbase.csv- Box A — matched cleanly with reported basis
- Box B — matched but basis missing or fields don't match (correction needed)
- Box C — not reported on the 1099-DA at all
The output PDF contains separate page groups per box.
Per-box PDF output
--per-box writes one PDF per checkbox category instead of a single merged file. The IRS typically expects separate Form 8949 submissions per box, so this is often the more correct filing shape.
daybook export 2025 --format 8949 --1099da coinbase.csv --per-box
# Writes: daybook-2025-FIFO-8949-boxA.pdf, ...-boxC.pdfFive new exchange CSV adapters
- OKX — V2 unified-account trade history (multi-row grouped by
Order id), V1 legacy trades (BOM/CR-tolerant), funding/deposit/withdrawal history. - Bybit — spot trade history (multi-fill grouping by
Order ID), funding v2 and v1 exports. Chinese side values (买入/卖出) are recognized. - MEXC — spot trade history with packed
"0.123USDT"fee cells, spot order history, deposits, and withdrawals. - Gate.io — unified Billing Details ledger. Trades reconstructed from grouped
action_datalegs; handles the literalOrder Fullfilledtypo emitted by Gate. - Bitget — spot trade history (UI and API-style headers), deposits, withdrawals. Tolerates legacy
_SPBLsymbol suffixes and 13-digit Unix ms timestamps.
All five auto-detect their profile by header signature. Status-filtering emits warnings rather than silently dropping rows.
1099-DA CSV parser
- Recognizes IRS box numbers (
1a–1g) and a variety of human-readable column names. - Handles US (
$1,234.56), European (1.234,56), and European-thousands (1.234.567) money formatting. - Normalizes Unicode minus (U+2212), en-dash, em-dash, and non-breaking hyphen to ASCII
-. - Warns on genuinely ambiguous formats (
1,23) rather than silently picking a parse.
Internal improvements
Shared CSV helpers module
packages/sources/src/_shared/csv-helpers.ts is the new home for parseAmount, parseTimestamp, normalizeAsset, normalizeHeader, assetLeg, pick, suffixDuplicateIds, sanitizeNativeId, hashRows, parseCsvRows, hashString, the FIAT_CURRENCIES set, and the NormalizedRow / CsvRow types.
All 10 adapters that use these helpers now import them instead of inlining their own copies — net reduction of ~525 lines across the source tree. The parseAmount function accepts an optional { zeroAsUndefined: true } for legacy adapters that treat zero-amount rows as noise.
Bipartite reconcile matcher
The original greedy matcher could strand reconcilable disposals when input order put them after a disposal that claimed their only candidate. v0.4.0 replaces it with edge-sorted greedy bipartite matching, which fixes the common DCA pathology where two same-day disposals compete for two same-day 1099-DA rows.
Shared pricing-chain helper
Both export and reconcile commands now use packages/cli/src/pricing-chain.ts for buildPricingChain + hydratePrices. Removes ~30 lines of provider-setup duplication.
CLI guardrails
daybook export --1099dasurfaces parser warnings, mismatches, missing-in-daybook count, and the recommended-checkbox reason inline.--per-boxwithout--1099dawarns that it will produce a single useless file.- MEXC and Bitget status-filtered rows now emit warnings instead of silently dropping.
Bug fixes
recommendCheckboxreturnedC("nothing to reconcile") when daybook had zero disposals but the 1099-DA had rows — a silent tax-under-reporting path. Now returnsBwith a clear "you may have missed importing data" message.- MEXC trade IDs no longer hash the full row (which changed if MEXC added columns); they hash only load-bearing fields so re-imports stay idempotent.
- Gate.io adapter no longer keeps an unused
descsSet.
Packaging
- CLI
package.jsonnow has an explicitfilesallowlist (dist/,README.md,LICENSE) andengines.node >= 20. - Repo is public on GitHub;
SECURITY.mdandCONTRIBUTING.mdadded.
npm install -g @gfargo/daybookTest suite
655 tests across 47 test files.
Known limitations carried forward
parseForm8949Pdfonly round-trips the first logical page-pair due to a pdf-libcopyPageslimitation that drops AcroForm field definitions across documents. The--per-boxrendering mode is the practical workaround — each emitted file is single-box and self-contained.- Edge-sorted bipartite matching is a 1/2-approximation in the worst case. In practice with daybook's tight default tolerances this is optimal; the documented pathological case is pinned with a regression test.
- OKX V2 sign handling (#35) and Bybit
Exec Valuedesync (#36) need real sample CSVs to verify before fixing.
License
MIT
Full Changelog: v0.3.0...v0.4.0