Releases: gfargo/daybook
Release list
v0.4.0 — 1099-DA reconciliation + global exchange coverage
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
v0.3.0 — Tax forms, NFT lots, broader ingestion
daybook v0.3.0
Tax form generation, NFT cost-basis tracking, and broader ingestion coverage.
New features
IRS tax forms and TXF export
- Form 8949 PDF generation with continuation sheets.
- Schedule D PDF generation from the same computed tax result.
- TXF v042 export for TurboTax import workflows.
daybook exportsupports--format csv|8949|schedule-d|txf.- Form 8949 checkbox selection is available with
--8949-checkbox A|B|C.
NFT cost-basis tracking
- ERC-721 and ERC-1155 acquisitions and disposals are classified separately from fungible assets.
- NFT lots are tracked individually by contract address and token ID.
- NFT cost basis can be derived from counterpart ETH/ERC-20 legs in the same transaction.
- Manual NFT price overrides use the
<contractAddress>:<tokenId>override key format. daybook events list --type nft_acquisitionand--type nft_disposalfilter NFT ledger entries.
Coinbase API sync
daybook sync --source coinbase(no--file) pulls directly from Coinbase using CDP keys (ECDSA / ES256).- Accounts and transactions fetched via the Coinbase App Track APIs, then enriched with Advanced Trade fills.
- Per-account sync watermark is stored after a successful run; subsequent syncs are incremental.
- CSV import via
--source coinbase --file <path>continues to work as a fallback.
New CSV adapters
- Binance and Binance.US —
daybook sync --source binance --file <path>/--source binance-us --file <path>. Recognizes the unified ledger export (UserID, UTC_Time, Account, Operation, Coin, Change) and the Binance.US tax-report format. - Crypto.com — App, Exchange, and DeFi wallet exports auto-detected from header signature.
- Gemini — transaction-history exports, including both the wide column shape (
BTC Amount BTC,Fee BTC BTC, etc.) and the standard narrow shape. - Robinhood — crypto transaction history with asset-name-to-symbol resolution (
Bitcoin→BTC,Ethereum→ETH, etc.).
Generic CSV importer
--source csv --file <path>accepts an exchange-neutral ledger format for anything not natively supported.- Expected columns:
Date, Sent Amount, Sent Currency, Received Amount, Received Currency, Fee Amount, Fee Currency, Label, Description, TxHash. - Tolerant column aliases (
Type/Label,Sent Quantity/Sent Amount, etc.).
Additional EVM chains
--sourcenow acceptsarbitrum,base,optimism, andbnbin addition toethandpolygon.- All use the existing Alchemy provider interface — no new auth required.
- Etherscan remains the fallback for failed-gas detail when
--include-failed-gasis set.
Packaging
- The npm package is scoped as
@gfargo/daybookto avoid the unrelated existingdaybookpackage on npm. - The installed binary remains
daybook.
npm install -g @gfargo/daybookTest suite
517 tests across 36 test files.
What's not in this release
- 1099-DA reconciliation (lands in v0.4.0)
- OKX, Bybit, MEXC, Gate.io, Bitget CSV adapters (land in v0.4.0)
- DeFi position classification (LP, lending, staking)
- Solana, Bitcoin, and other non-EVM chains
License
MIT
Full Changelog: v0.2.0...v0.3.0
v0.2.0
What's Changed
Design system implementation, a new cost-basis method, and CLI polish across the board.
New features
LIFO cost-basis method
LIFO (Last In, First Out) joins FIFO and HIFO as a supported cost-basis strategy. LIFO disposes the most recently acquired lots first — useful in rising markets where recent purchases carry higher cost basis.
daybook export 2024 --method LIFOdaybook compare now shows all three methods side by side:
Metric │ FIFO HIFO LIFO
────────────────────┼─────────────────────────────────────
Disposal count │ 47 47 47
Short-term gain │ $12,400 $4,200 $8,100
Long-term gain │ $4,760 $4,760 $4,760
Total taxable │ $17,160 $8,960 $12,860
Income │ $1,205 $1,205 $1,205
JSON output on all read commands
Every read command now accepts --format json for machine-readable output. Data serializes directly to stdout — no colors, no Ink rendering. Decimal values are preserved as strings.
daybook events list --format json | jq '.[] | select(.type == "trade")'
daybook events count --format json
daybook account list --format json
daybook overrides list --format json
daybook compare 2024 --format jsonUsage examples in help text
Every command now includes usage examples in its --help output. Help text has been rewritten to follow the design system's voice guidelines: sentence case, active voice, specific option descriptions.
daybook sync --help
daybook export --helpDesign system
This release implements the centralized UI component library specified in docs/cli-design-system.md. All CLI output now flows through a shared theme and component set.
Theme module (packages/cli/src/ui/theme.ts)
- 8 semantic color tokens from the ledger-paper palette (ink, paper, rule, note, gain, loss, caution, stamp)
- Spacing scale (0, 1, 2, 4 cells)
- Nerd Font glyph registry with ASCII fallback (controlled by
DAYBOOK_NO_NERDFONT=1) - Braille spinner animation frames
- Format helpers:
formatUsd(),formatCount(),truncateAddress(),pluralize()
9 Ink components
| Component | Purpose |
|---|---|
<Header> |
Bold + note-color section heading |
<Row> |
Label + value pair with shared label width |
<Glyph> |
Single icon from the glyph registry |
<Spinner> |
Animated braille spinner with label |
<Stat> |
Prominent single statistic |
<Table> |
Generic table with auto-width and column config |
<Section> |
Named group with indented children |
<EmptyState> |
Quiet "nothing to show" with hint |
<ErrorBlock> |
Structured error with recovery hint |
Commands migrated to design system
- CompareTable — uses
color.paperfor labels,color.notefor highlighted values,color.rulefor dividers. Now renders dynamically for any number of methods (not hardcoded to 2). - EventsTable — uses
color.stampfor source/type labels,EmptyStatefor empty results. - UnclassifiedReview — uses
glyph('chevron')for cursor,color.notefor selections,color.stampfor source labels. - LotPicker — uses
color.gain/color.cautionfor holding period,glyph('check')for selected lots. - overrides list — migrated from manual
.padEnd()formatting to the shared<Table>component. - sync (Coinbase, Kraken, EVM) — migrated from
console.logto themed Ink output withHeader,Row,Section, status glyphs. - classify (normal + dry-run) — migrated from
console.logto themed Ink output with type/rule breakdowns and unclassified warnings.
Non-TTY and degradation
- TTY detection utilities (
isTTY(),terminalWidth(),isNarrowTerminal(),requireInteractive()) for graceful degradation when piped or in CI. - chalk auto-handles
NO_COLOR=1andTERM=dumb. - Glyph registry falls back to ASCII when
DAYBOOK_NO_NERDFONT=1is set. - Interactive prompts (classify
--review, export--method specific-id) already guard against non-TTY with clear error messages suggesting the non-interactive alternative.
Test suite
214 tests across 15 test files (was 205 in v0.1.0). New tests cover LIFO strategy selection, FIFO/HIFO sanity checks, and updated compare integration tests for 3-method output.
Release tooling
v0.2.0 is the first release using the new release-it workflow:
pnpm release # interactive version prompt, typecheck, test, build, tag, publish
pnpm release:dry # preview without side effectsv0.1.0
First public release. A self-hosted CLI for crypto wallet auditing and tax reporting.
Pull transactions from Coinbase, Kraken, and EVM wallets, normalize them into a single ledger, classify events automatically, compute cost basis, and export a tax-ready CSV.
Data sources
- Coinbase — CSV import with automatic pair-merging for internal moves
- Kraken — CSV import with trade pairing by refid and asset normalization (XXBT→BTC, XETH→ETH, etc.)
- Ethereum mainnet — via Alchemy SDK (external, internal, and ERC-20/721/1155 transfers in one paginated call)
- Polygon — via Alchemy SDK (same provider interface as Ethereum)
- Failed transaction gas — optional Etherscan provider captures gas spent on reverted EVM transactions (
--include-failed-gas)
All syncs are idempotent. Re-running with the same data is a no-op.
Event classification
A 7-rule chain processes raw events into typed ledger entries:
- Coinbase pair merger — collapses staking transfer / Eth2 deprecation pairs
- Coinbase self-transfer detection — parses Send notes, matches to user's own addresses
- Cross-source matching — fuzzy match between exchange withdrawals and on-chain receives (±10 min, ±0.5%)
- DEX swap collapse — groups transfers by txHash against a curated router catalog (Uniswap V2/V3, MetaMask Swap Router, QuickSwap)
- Bridge detection — matches outbound bridge transactions to destination-chain receives within a 24h window (Celer cBridge, Polygon PoS Bridge)
- Approval gas accounting — produces
fee_disposalentries for token approve() calls - Default passthrough — direct mapping for everything else
Manual overrides are first-class and survive re-classification.
Tax engine
- Cost-basis methods: FIFO (default), HIFO, and Specific ID with an interactive terminal lot picker
- Specific ID replay: save lot selections to JSON, replay them on future exports (
--lot-selections) - Wash sale flagging: flags loss disposals where an acquisition of the same asset occurs within ±30 calendar days (informational only, no disallowance computation)
- Pricing chain: source-reported price → CoinGecko historical API → manual override, with SQLite caching
- Asset aliasing: POL↔MATIC and ETH2↔ETH treated as equivalent for cost-basis purposes
- CSV export: one row per disposal with proceeds, cost basis, gain/loss, short/long-term split, holding period, and optional Wash Sale? column
- Method comparison:
daybook compare <year>runs FIFO and HIFO side-by-side so you can pick the better outcome before exporting
CLI commands
daybook init Create config + database
daybook account add <id> Add a source account
daybook account list List configured accounts
daybook sync --source <src> Ingest transactions
daybook events count Count events by type
daybook events list Browse events (Ink table with filters)
daybook classify Run classifier rules
daybook classify --dry-run Preview classification without writing
daybook classify --review Interactively review unclassified events
daybook export <year> Export tax-ready CSV
daybook compare <year> Compare FIFO vs HIFO
daybook overrides set <asset> ... Set a manual price override
daybook overrides list List all overrides
daybook overrides remove <id> Remove an override
Incremental sync
EVM sources support --from <date|block> for incremental syncing:
daybook sync --source eth --from 2024-01-01
daybook sync --source eth --from 19000000Install
npm install -g daybookRequires Node.js 20+. EVM sync requires ALCHEMY_API_KEY. Failed-gas tracking requires ETHERSCAN_API_KEY. CoinGecko pricing works without an API key (public rate limits apply).