Crypto tax lot cost-basis accounting library
Project description
coinbasis
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 daysboundary - Tax-year reports:
CapitalGainsReport(Form 8949-shaped),IncomeReport,PortfolioReportwith 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": {...}});Decimalvalues 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file coinbasis-0.1.0.tar.gz.
File metadata
- Download URL: coinbasis-0.1.0.tar.gz
- Upload date:
- Size: 37.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
952ad98c6782eb8f7f2fbdba6806f79c0eb2ba23480093d4a6982441b32534e6
|
|
| MD5 |
ad6a5ddc6f34ddb363dd37184e208277
|
|
| BLAKE2b-256 |
1fe38a94a46b66fe97649caec5ff3e80e2a9d5b5c9e7d187587c4f8b7d83ae4c
|
Provenance
The following attestation bundles were made for coinbasis-0.1.0.tar.gz:
Publisher:
publish.yml on Technical-1/coinbasis-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coinbasis-0.1.0.tar.gz -
Subject digest:
952ad98c6782eb8f7f2fbdba6806f79c0eb2ba23480093d4a6982441b32534e6 - Sigstore transparency entry: 1722612974
- Sigstore integration time:
-
Permalink:
Technical-1/coinbasis-py@cb99b1b563cc0b42884b0f1365e5c7088ae08dbc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Technical-1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cb99b1b563cc0b42884b0f1365e5c7088ae08dbc -
Trigger Event:
release
-
Statement type:
File details
Details for the file coinbasis-0.1.0-py3-none-any.whl.
File metadata
- Download URL: coinbasis-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
336e4c4a25e0dc142d39147cdcaffa2c7209ce9cefeea197841a2779a346aea3
|
|
| MD5 |
f0af8b3bbb42fd521350d856553a0c9f
|
|
| BLAKE2b-256 |
29bec33368770d2152385f2a1188fa76b095e96fbcafc89ec81f1a1e6f4dbb28
|
Provenance
The following attestation bundles were made for coinbasis-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Technical-1/coinbasis-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coinbasis-0.1.0-py3-none-any.whl -
Subject digest:
336e4c4a25e0dc142d39147cdcaffa2c7209ce9cefeea197841a2779a346aea3 - Sigstore transparency entry: 1722613045
- Sigstore integration time:
-
Permalink:
Technical-1/coinbasis-py@cb99b1b563cc0b42884b0f1365e5c7088ae08dbc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Technical-1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cb99b1b563cc0b42884b0f1365e5c7088ae08dbc -
Trigger Event:
release
-
Statement type: