Skip to main content

coinbasis

PyPI version Python versions License: MIT OR Apache-2.0 CI

A pure-Python, stdlib-only crypto tax-lot cost-basis accounting engine.

I built this library to provide a correct, dependency-free accounting engine in Python: eight transaction event types, five cost-basis methods, IRS gift dual-basis rules, per-wallet lot pools, and progressive tax-bracket estimation — all without a single runtime dependency and with Decimal arithmetic throughout for exact decimal precision.

Features

  • Five cost-basis methods: FIFO, LIFO, HIFO, Average Cost, Specific ID
  • Eight transaction types: Buy, Sell, Trade, Income, Spend, Transfer, GiftSent, GiftReceived
  • Per-wallet lot pooling: disposals draw only from the named wallet; transfers move lots between wallets preserving original basis and holding-period clock
  • IRS gift dual-basis rule: GiftReceived lots carry donor basis and FMV; the engine applies the correct gain/loss/dead-zone calculation at disposal time
  • Short/long-term classification: strict > 365 days boundary
  • Tax-year reports: CapitalGainsReport (Form 8949-shaped), IncomeReport, PortfolioReport with unrealized P&L and allocation percentages
  • Progressive tax estimation: configurable brackets + short-term flat rate; US preset included via TaxConfig.default()
  • Portfolio statistics: period returns, volatility, Sharpe ratio, max drawdown, cumulative return
  • JSON round-trip: externally-tagged JSON schema ({"Buy": {...}}); Decimal values serialized as strings for unambiguous round-trip
  • Zero runtime dependencies: stdlib only (decimal, dataclasses, datetime, json, enum, math)

Install

pip install coinbasis

Quickstart

from decimal import Decimal
from datetime import datetime, timezone
from coinbasis import Portfolio, CostBasisMethod, Buy, Sell

def dt(y, m, d):
    return datetime(y, m, d, tzinfo=timezone.utc)

txs = [
    Buy(timestamp=dt(2020, 1, 1), wallet="hot", asset="btc",
        quantity=Decimal("1"), unit_price=Decimal("100"), fee=Decimal("0")),
    Buy(timestamp=dt(2021, 1, 1), wallet="hot", asset="btc",
        quantity=Decimal("1"), unit_price=Decimal("300"), fee=Decimal("0")),
    Sell(timestamp=dt(2022, 1, 1), wallet="hot", asset="btc",
         quantity=Decimal("1"), unit_price=Decimal("500"), fee=Decimal("0")),
]

p = Portfolio.from_transactions(txs)

# FIFO: matches oldest lot first -> gain 400
fifo_gain = sum(g.gain for g in p.realized_gains(CostBasisMethod.FIFO))
print(fifo_gain)       # Decimal('400')

# HIFO: matches highest-cost lot first -> gain 200
hifo_gain = sum(g.gain for g in p.realized_gains(CostBasisMethod.HIFO))
print(hifo_gain)       # Decimal('200')

# Tax-year capital gains report
report = p.capital_gains_report(CostBasisMethod.FIFO, tax_year=2022)
print(report.long_term_gain)   # Decimal('400')
print(report.short_term_gain)  # Decimal('0')

Income and portfolio valuation

from coinbasis import Income, IncomeSource, Transfer
from coinbasis.tax import TaxConfig

# Staking income creates a lot at its FMV and records an IncomeEvent
txs = [
    Income(timestamp=dt(2021, 1, 1), wallet="hot", asset="eth",
           quantity=Decimal("2"), value=Decimal("200"), source=IncomeSource.STAKING),
]
p = Portfolio.from_transactions(txs)
print(p.income_report(2021).total_income)  # Decimal('200')

# Valuation against live prices
report = p.valuation(CostBasisMethod.FIFO, {"eth": Decimal("150")})
print(report.total_unrealized)  # Decimal('100')

# Tax estimate
from coinbasis.tax import estimate, TaxConfig
est = estimate(short_gain=Decimal("10000"), long_gain=Decimal("50000"),
               config=TaxConfig.default())
print(est.total_tax)

JSON round-trip

from coinbasis.serialization import ledger_to_json, ledger_from_json

json_str = ledger_to_json(txs, indent=2)
restored = ledger_from_json(json_str)

Examples

Runnable scripts live in examples/. Each is self-contained and offline (coinbasis has no network dependency) and prints clearly labeled output. Run any of them with python examples/<name>.py.

Example Shows
quickstart.py Buy 1 BTC, sell it, read the realized gain
cost_basis_methods.py FIFO / LIFO / HIFO / Average on one ledger, each yielding a different gain
wallet_transfers.py A transfer preserving original basis and the holding-period clock
gifts.py Gift dual-basis with a gain case and a loss case
income.py Staking / airdrop income creating lots at FMV plus an income report
valuation.py PortfolioReport unrealized P&L and allocation at supplied prices
tax_estimate.py CapitalGainsReport plus a progressive TaxEstimate
portfolio_stats.py stats module: returns, volatility, Sharpe, drawdown, cumulative return
json_roundtrip.py Serialize a ledger to JSON and read it back losslessly

Development

git clone https://github.com/Technical-1/coinbasis-py
cd coinbasis-py
pip install -e ".[dev]"
python3 -m pytest          # run full test suite
python3 -m ruff check src/ # lint
python3 -m build           # build sdist + wheel

Project Structure

src/coinbasis/
  __init__.py      Public API surface — all re-exports
  errors.py        PortfolioError exception hierarchy (8 typed errors)
  transaction.py   Transaction (8 frozen dataclass variants) + IncomeSource
  method.py        CostBasisMethod enum, LotPick, LotSelection, order_for()
  lot.py           Internal Lot + GiftBasis dataclasses
  engine.py        Internal ledger-replay engine (_Engine, run())
  report.py        Term, RealizedGain, IncomeEvent, Holding, PortfolioReport, ...
  portfolio.py     Portfolio facade — public query methods
  tax.py           TaxBracket, TaxConfig, TaxEstimate, estimate()
  stats.py         returns_from_values, volatility, sharpe_ratio, max_drawdown, cumulative_return
  serialization.py JSON (de)serialization — externally-tagged transaction schema

tests/
  test_engine.py         Engine unit tests
  test_end_to_end.py     End-to-end numeric checks over representative ledgers
  test_portfolio.py      Portfolio facade tests
  test_serialization.py  JSON round-trip tests
  test_stats.py          Statistics module tests
  test_tax.py            Tax estimation tests
  test_properties.py     Property-based invariant tests
  (+ more)

Not tax advice

This library computes cost-basis and gain estimates based on the inputs you provide. Consult a qualified tax professional for advice specific to your situation.

License

MIT OR Apache-2.0

Author

Jacob Kanfer — GitHub: Technical-1

Release files for coinbasis 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for coinbasis 0.1.0
File Size Uploaded
coinbasis-0.1.0.tar.gz 37.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for coinbasis 0.1.0
File Interpreter ABI Platform
coinbasis-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 62.9 kB

Release files / coinbasis-0.1.0.tar.gz

Download URL coinbasis-0.1.0.tar.gz
Size 37.6 kB
Tags Source
SHA-256 checksum
How to use checksums
952ad98c6782eb8f7f2fbdba6806f79c0eb2ba23480093d4a6982441b32534e6
BLAKE2b-256 checksum
How to use checksums
1fe38a94a46b66fe97649caec5ff3e80e2a9d5b5c9e7d187587c4f8b7d83ae4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 4, 2026.

Transparency log

Release files / coinbasis-0.1.0-py3-none-any.whl

Download URL coinbasis-0.1.0-py3-none-any.whl
Size 25.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
336e4c4a25e0dc142d39147cdcaffa2c7209ce9cefeea197841a2779a346aea3
BLAKE2b-256 checksum
How to use checksums
29bec33368770d2152385f2a1188fa76b095e96fbcafc89ec81f1a1e6f4dbb28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page