Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.cache/
target/
AGENTS.md
.claude

# Symlink to Bitcoin Core
bitcoin-core
Expand Down
141 changes: 141 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "MIT"
[dependencies]
anyhow = "1.0.97"
chrono = { version = "0.4.40", default-features = false, features = ["clock", "std"] }
csv = "1.3.1"
dotenvy = "0.15.7"
fixed_decimal = "0.7.1"
icu_decimal = "2.1.1"
Expand All @@ -15,6 +16,7 @@ quick-xml = "0.37"
reqwest = { version = "0.12.15", default-features = false, features = ["blocking", "json", "rustls-tls", "socks"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
zip = { version = "2.2.3", default-features = false, features = ["deflate"] }

[dev-dependencies]
bitcoin-capnp-types = { git = "https://github.com/2140-dev/bitcoin-capnp-types" }
Expand Down
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Rust CLI for Bitcoin accounting tasks.

Current commands: `received-value`, `export`, `reconstruct`.
Current commands: `received-value`, `cache-rates`, `export`, `reconstruct`.

## Available commands

Expand All @@ -18,6 +18,40 @@ The command works as follows:
4. Uses the smallest Kraken `OHLC` candle interval that can still cover the transaction confirmation time (override with `--candle <minutes>`).
5. Estimates the value of the received BTC in the quote currency of the chosen Kraken pair.

### `cache-rates`

Populate `.cache/rates.json` for one UTC calendar year of Bitcoin rates.

```bash
cargo run -- cache-rates 2024
cargo run -- cache-rates --vwap 2024
cargo run -- cache-rates --vwap --candle 60 2024
```

The command works as follows:

1. Without `--vwap`, fetches the daily (`1440`) Kraken `OHLC` data that is still available through the public API.
2. Detects which closed UTC candles in the requested year are still missing because they are older than Kraken's 720-candle retention window.
3. Backfills those missing candles into `.cache/rates.json` from one of Kraken's downloadable archives:
the default OHLCVT archive, or the larger time-and-sales archive when `--vwap` is set.

By default, the cache keys are written as normal `1440`-minute entries. With `--vwap`, the command writes entries at `DEFAULT_CANDLE_MINUTES` if configured, or `1440` otherwise; `--candle <minutes>` overrides that in the normal way.

Trade-off:

- Recent days use Kraken's daily `OHLC` API `vwap`, just like the normal live lookup path.
- Without `--vwap`, older archive-backed days use the daily `(open + close) / 2` midpoint derived from Kraken's OHLCVT CSV, because the downloadable OHLCVT archive does not include `vwap`.
- Without `--vwap`, the command only fills missing cache entries for that year and interval; it does not overwrite existing entries with midpoint-derived values.
- With `--vwap`, the command computes exact Kraken VWAP candles at the chosen interval from the downloadable time-and-sales trade archive (`timestamp,price,volume`) and overwrites any existing cache entries for that year and interval.
- With `--vwap`, the command first checks whether the public API still covers part of the requested year at the chosen interval. If quarterly trade archives are available for the rest, it keeps the API slice and downloads only the missing quarters. If the complete trade archive is inevitable anyway, it skips the API and computes the whole year from the archive.
- With `--vwap`, the command tries quarterly trade archives first and falls back to Kraken's complete trade archive automatically if that year's quarterly trade ZIPs are not published.
- Downloaded Kraken archive ZIPs are kept under `.cache/kraken/` and reused across later runs; the command does not keep expanded CSV files on disk.
- `--vwap` is substantially heavier because Kraken's trade archives are much larger than the OHLCVT archives, especially when the complete trade archive ZIP is needed instead of quarterly updates.

For years whose quarterly trade ZIPs are not published, `cache-rates --vwap`
falls back to Kraken's complete trade archive, which requires a ~12G download.
But it does mean VWAP values can be constructed as far back as late 2013.

### `export`

<p>
Expand Down Expand Up @@ -46,6 +80,7 @@ Key options:
- `--mark-to-market` — add year-end reconciliation entries (default on in fiat mode, can be combined with `--fifo`)
- `--output <file>` — output file path (appends if file exists)
- `--start-date <YYYY-MM-DD>` — only include transactions from this date
- `--candle <minutes>` — Kraken candle interval (`DEFAULT_CANDLE_MINUTES` or `1440` by default)
- `--datadir <path>` — Bitcoin Core data directory (for cookie auth)
- `--chain <name>` — chain: main, testnet3, testnet4, signet, regtest (default: main)

Expand Down Expand Up @@ -168,6 +203,8 @@ transaction_age <= 720 * interval_minutes * 60

If the transaction is too old to fit inside Kraken's `1d` candle retention window, the tool exits with an error instead of silently switching to a coarser interval.

`cache-rates` is the explicit opt-in workaround for older values: by default it backfills `.cache/rates.json` from Kraken's OHLCVT archive as daily `(open + close) / 2` midpoint prices, or with `--vwap` it computes exact Kraken VWAP candles at the chosen interval from the larger trade archive.

## Development

See [DEVELOP.md](DEVELOP.md) for Bitcoin Core build instructions, running
Expand All @@ -191,6 +228,7 @@ Licensed under the MIT License. See `LICENSE` for details.
- `src/main.rs` — top-level command dispatcher
- `src/lib.rs` — library crate root
- `src/commands/received_value.rs` — `received-value` subcommand
- `src/commands/cache_rates.rs` — `cache-rates` subcommand
- `src/commands/export.rs` — `export` subcommand
- `src/commands/reconstruct.rs` — `reconstruct` subcommand
- `src/common.rs` — shared config, mempool, Kraken, Tor, candle, and formatting logic
Expand Down
Loading
Loading