Skip to content

Releases: gfargo/daybook

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

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

v0.3.0 — Tax forms, NFT lots, broader ingestion

Choose a tag to compare

@gfargo gfargo released this 23 May 19:13
6b11497

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 export supports --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_acquisition and --type nft_disposal filter 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.USdaybook 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 (BitcoinBTC, EthereumETH, 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

  • --source now accepts arbitrum, base, optimism, and bnb in addition to eth and polygon.
  • All use the existing Alchemy provider interface — no new auth required.
  • Etherscan remains the fallback for failed-gas detail when --include-failed-gas is set.

Packaging

  • The npm package is scoped as @gfargo/daybook to avoid the unrelated existing daybook package on npm.
  • The installed binary remains daybook.
npm install -g @gfargo/daybook

Test 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

Choose a tag to compare

@gfargo gfargo released this 03 May 20:28

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 LIFO

daybook 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 json

Usage 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 --help

Design 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.paper for labels, color.note for highlighted values, color.rule for dividers. Now renders dynamically for any number of methods (not hardcoded to 2).
  • EventsTable — uses color.stamp for source/type labels, EmptyState for empty results.
  • UnclassifiedReview — uses glyph('chevron') for cursor, color.note for selections, color.stamp for source labels.
  • LotPicker — uses color.gain/color.caution for 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.log to themed Ink output with Header, Row, Section, status glyphs.
  • classify (normal + dry-run) — migrated from console.log to 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=1 and TERM=dumb.
  • Glyph registry falls back to ASCII when DAYBOOK_NO_NERDFONT=1 is 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 effects

v0.1.0

Choose a tag to compare

@gfargo gfargo released this 03 May 16:41

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:

  1. Coinbase pair merger — collapses staking transfer / Eth2 deprecation pairs
  2. Coinbase self-transfer detection — parses Send notes, matches to user's own addresses
  3. Cross-source matching — fuzzy match between exchange withdrawals and on-chain receives (±10 min, ±0.5%)
  4. DEX swap collapse — groups transfers by txHash against a curated router catalog (Uniswap V2/V3, MetaMask Swap Router, QuickSwap)
  5. Bridge detection — matches outbound bridge transactions to destination-chain receives within a 24h window (Celer cBridge, Polygon PoS Bridge)
  6. Approval gas accounting — produces fee_disposal entries for token approve() calls
  7. 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 19000000

Install

npm install -g daybook

Requires 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).