Skip to content

Pricing

Griffen Fargo edited this page May 9, 2026 · 2 revisions

Pricing

The pricing module lives in packages/tax/src/pricing/. It resolves USD prices for assets at specific timestamps using a priority chain of providers.

Priority Chain

Providers are tried in order. The first provider that returns a price wins:

  1. Source-reported — prices the source itself reported (e.g., Coinbase's Price at Transaction column)
  2. CoinGecko — historical price lookup by symbol or contract address
  3. Manual override — user-provided prices in the price_overrides table

Providers

Source-Reported

File: pricing/providers/source-reported.ts

Extracts prices from RawEvent legs where the source provided a USD value (the usdReportedBySource field). This is the most defensible price source for tax purposes — it's what the exchange used at the moment of the transaction.

Available for: Coinbase transactions (all types), generic CSV rows with USD value columns, and exchange rows where the source export includes explicit USD values.

CoinGecko

File: pricing/providers/coingecko.ts

Fetches historical prices from the CoinGecko API:

  • Major assets: GET /coins/{id}/history?date=DD-MM-YYYY
  • ERC-20 tokens: GET /coins/ethereum/contract/{address}/market_chart/range

Handles rate limits with exponential backoff (max 3 retries on 429 responses). The free tier allows approximately 30 requests per minute.

Manual Override

File: pricing/providers/manual-override.ts

Reads from the price_overrides SQLite table. Use this for tokens that have no reliable price feed.

# Set a manual price
daybook overrides set SOMETOKEN 2024-03-15 0.50

# List all overrides
daybook overrides list

# Remove an override
daybook overrides remove <id>

Caching

File: pricing/cache.ts

Resolved prices are cached in the prices SQLite table, keyed by (asset, date). Cache hits skip the provider chain entirely. The cache is populated automatically as prices are resolved.

Asset Aliases

File: pricing/asset-aliases.ts

Some assets have multiple names that should be treated as equivalent for pricing:

Alias Canonical
POL MATIC
MATIC POL
ETH2 ETH

When looking up a price, the alias map is consulted so that POL and MATIC resolve to the same price.

Unpriced Assets

When no provider returns a price:

  • Assets worth less than $1 USD-equivalent are auto-zeroed (handles spam airdrops)
  • Assets above $1 prompt for manual override

The export flags unpriced events so they're visible in the output.

Adding a New Provider

Implement the PricingProvider interface:

interface PricingProvider {
  name: string;
  getPrice(asset: string, timestamp: Date): Promise<PriceResult | null>;
}

Then add it to the priority chain in the pricing configuration. The chain runner handles fallthrough automatically.

Clone this wiki locally