Skip to content

v0.4.0 — 1099-DA reconciliation + global exchange coverage

Latest

Choose a tag to compare

@gfargo gfargo released this 23 May 19:13

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 json for 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.pdf

Five 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_data legs; handles the literal Order Fullfilled typo emitted by Gate.
  • Bitget — spot trade history (UI and API-style headers), deposits, withdrawals. Tolerates legacy _SPBL symbol 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 (1a1g) 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 --1099da surfaces parser warnings, mismatches, missing-in-daybook count, and the recommended-checkbox reason inline.
  • --per-box without --1099da warns that it will produce a single useless file.
  • MEXC and Bitget status-filtered rows now emit warnings instead of silently dropping.

Bug fixes

  • recommendCheckbox returned C ("nothing to reconcile") when daybook had zero disposals but the 1099-DA had rows — a silent tax-under-reporting path. Now returns B with 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 descs Set.

Packaging

  • CLI package.json now has an explicit files allowlist (dist/, README.md, LICENSE) and engines.node >= 20.
  • Repo is public on GitHub; SECURITY.md and CONTRIBUTING.md added.
npm install -g @gfargo/daybook

Test suite

655 tests across 47 test files.

Known limitations carried forward

  • parseForm8949Pdf only round-trips the first logical page-pair due to a pdf-lib copyPages limitation that drops AcroForm field definitions across documents. The --per-box rendering 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 Value desync (#36) need real sample CSVs to verify before fixing.

License

MIT


Full Changelog: v0.3.0...v0.4.0