coinlytics
Python library for crypto portfolio analytics and market data — sits above the coinbasis cost-basis engine and handles all networking, caching, and analytics an app needs to run.
I built coinlytics as the data and analytics layer for a multi-tier crypto tracker ecosystem. It provides a resilient CoinGecko price client (keyless by default, automatic keyed fallback on HTTP 429, exponential backoff, TTL on-disk cache, and offline last-good serving), tax-aware portfolio rebalancing that simulates sells through coinbasis to estimate HIFO realized gains, daily portfolio value/P&L reconstruction by replaying the transaction ledger, performance metrics via coinbasis.stats, staking yield analytics via DefiLlama, and a news sentiment feed sourced from RSS and CryptoPanic. The package is intentionally minimal — it depends only on requests and coinbasis, with no ML or heavy analytics libraries.
Features
- Resilient CoinGecko client — tries the public keyless endpoint first; on HTTP 429 automatically retries with a configured Demo or Pro API key (routing the correct base URL and auth header); exponential backoff with
Retry-Afterheader support; TTL on-disk JSON cache (atomic writes); offline last-good fallback withPriceBook.staleflag. MockClient— satisfies the samePriceSourceprotocol as the real client; deterministic, no network; afail_idsset exercises error-handling paths without mockingrequests.- Tax-aware rebalancing — Band and Full strategies; drift calculation vs. target weights (equal, market-cap, or custom); HIFO realized-gain estimate for each proposed sell, computed by appending a simulated
Sellto a throwawaycoinbasis.Portfolioand reading the gain delta. - Historical P&L reconstruction — replays the full transaction ledger up to each target date using
coinbasis.Portfolio.from_transactions, then values holdings at per-coin historical prices. - Performance metrics — daily
Snapshotdataclass; delegates volatility, Sharpe ratio, max drawdown, and cumulative return tocoinbasis.stats. - DefiLlama staking APY — fetches pool yields for a list of symbols; selects the highest-TVL exact-match pool per symbol.
- News + sentiment —
fetch_rssandfetch_cryptopanicwith a DOCTYPE entity-expansion guard (stdlib only, nodefusedxml); naive keyword-lexicon sentiment classification (bullish/bearish/neutral) using whole-word regex matching. - Analytics helpers — Pearson correlation, correlation matrix, portfolio volatility, annualized volatility.
Tech Stack
| Layer | Choice |
|---|---|
| Language | Python >= 3.10 |
| Cost-basis engine | coinbasis >= 0.1, < 0.2 |
| HTTP | requests >= 2.28, < 3 |
| XML parsing | xml.etree.ElementTree (stdlib) |
| Money math | decimal.Decimal throughout |
| Build | setuptools >= 68 |
| Lint / format | ruff |
| Tests | pytest |
Getting Started
pip install coinlytics
For local development against an unpublished coinbasis:
pip install -e ../coinbasis-py # local coinbasis first
pip install -e ".[dev]" # coinlytics + pytest + ruff
Fetch current prices (keyless, no API key needed)
from coinlytics import CoinGeckoClient, CoinGeckoConfig
cfg = CoinGeckoConfig(cache_dir="~/.cache/coinlytics")
client = CoinGeckoClient(cfg)
pb = client.prices(["bitcoin", "ethereum"])
print(pb.quotes["bitcoin"].price) # decimal.Decimal
print(pb.stale) # True if served from offline cache
prices_map = pb.prices_map() # dict[str, Decimal] for coinbasis.Portfolio.valuation()
With a Demo API key (keyed fallback on 429)
cfg = CoinGeckoConfig(
api_key="your-demo-key",
plan="demo",
cache_dir="~/.cache/coinlytics",
cache_ttl=120,
)
client = CoinGeckoClient(cfg)
Tax-aware rebalancing
from decimal import Decimal
from coinlytics import compute_trades, target_weights, RebalanceStrategy
import coinbasis
# portfolio is a coinbasis.Portfolio built from your ledger
weights = target_weights("equal", ["bitcoin", "ethereum"])
plan = compute_trades(
current_values={"bitcoin": Decimal("70000"), "ethereum": Decimal("30000")},
target_weights_map=weights,
prices={"bitcoin": Decimal("50000"), "ethereum": Decimal("3000")},
strategy=RebalanceStrategy.BAND,
band=Decimal("0.05"),
portfolio=portfolio, # optional: attaches TaxEstimate to sells
)
for action in plan.actions:
print(action.asset, action.side, action.amount_usd)
if action.tax:
print(" estimated HIFO gain:", action.tax.realized_gain)
Historical portfolio value/P&L
from coinlytics import reconstruct_series
series = reconstruct_series(
txs=portfolio.transactions,
price_by_coin_date={"bitcoin": {"2024-01-01": 42000.0, "2024-01-02": 43500.0}},
dates=["2024-01-01", "2024-01-02"],
)
for day in series:
print(day["date"], day["value"], day["pl"])
News sentiment
from coinlytics import fetch_rss, filter_items, sentiment_summary, keywords_for
items = fetch_rss("https://cointelegraph.com/rss")
btc_items = filter_items(items, keywords_for("bitcoin", {}))
summary = sentiment_summary(btc_items)
print(summary["overall"]) # 'bullish' | 'bearish' | 'neutral'
Examples
Runnable scripts live in examples/ — one self-contained script per file. Run any of them with python examples/<name>.py.
The offline examples need no network and no API key:
| Example | What it shows |
|---|---|
mock_client.py |
MockClient as a drop-in PriceSource — prices, history, market caps, sparklines, and the fail_ids error path. Fully offline. |
rebalance.py |
Tax-aware rebalancing: builds a coinbasis ledger, computes trades toward equal weights, and attaches a HIFO TaxEstimate to each sell. |
performance.py |
Daily Snapshot history, dedup_append, and metrics (volatility, Sharpe, max drawdown, cumulative return) via coinbasis.stats. |
history.py |
Daily value/P&L reconstruction by replaying a ledger with reconstruct_series, plus point-in-time holdings_as_of. |
staking.py |
Effective APYs (API match vs. manual fallback), projected yield, rewards summary, and combined P&L. |
news.py |
Keyword filtering (whole-word) and lexicon sentiment (classify_sentiment, sentiment_summary) over news-item dicts. |
The two live-price examples hit the network (and are clearly marked); they guard their network calls so they print a friendly message rather than crash when offline:
| Example | What it shows |
|---|---|
prices_keyless.py |
Real CoinGeckoClient against the public keyless endpoint. Needs internet. |
prices_with_key.py |
Real CoinGeckoClient with a Demo API key (keyed fallback on HTTP 429). Needs internet and COINGECKO_API_KEY. |
The offline examples are exercised by tests/test_examples.py; the network ones are skipped there.
Development
# Run tests
pytest
# Lint
ruff check src/ tests/
# Build a distribution
python -m build
Project Structure
coinlytics-py/
├── src/
│ └── coinlytics/
│ ├── __init__.py # public re-exports
│ ├── errors.py # typed exception hierarchy
│ ├── analytics.py # correlation, portfolio volatility, annualize
│ ├── defillama.py # DefiLlama APY fetcher
│ ├── history.py # ledger-replay P&L reconstruction
│ ├── news.py # keyword filter + lexicon sentiment
│ ├── perf.py # Snapshot, PerfMetrics, metrics()
│ ├── rebalance.py # Band/Full strategies + tax estimate
│ ├── rss.py # RSS + CryptoPanic fetchers
│ ├── staking.py # effective APY, projected yield, rewards summary
│ └── prices/
│ ├── __init__.py
│ ├── client.py # CoinGeckoClient (keyless→keyed→cache→offline)
│ ├── cache.py # DiskCache (TTL, atomic writes)
│ ├── mock.py # MockClient (PriceSource protocol, no network)
│ └── models.py # Quote, PriceBook, HistoryPoint, PriceSource
├── tests/
│ ├── conftest.py
│ ├── test_client.py
│ ├── test_cache.py
│ ├── test_mock_client.py
│ ├── test_rebalance.py
│ ├── test_history.py
│ ├── test_perf.py
│ ├── test_staking.py
│ ├── test_defillama.py
│ ├── test_rss.py
│ ├── test_news.py
│ └── test_analytics.py
├── pyproject.toml
└── README.md
License
MIT OR Apache-2.0
Author
Jacob Kanfer — github.com/Technical-1
Release files for coinlytics 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| coinlytics-0.1.0.tar.gz | 56.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| coinlytics-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 90.3 kB
Release files / coinlytics-0.1.0.tar.gz
| Download URL | coinlytics-0.1.0.tar.gz |
|---|---|
| Size | 56.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
46751a6f85e591758a91453f90a5547e3dfd9fbce047083f370cf40d38b1853f
|
|
BLAKE2b-256 checksum How to use checksums |
53161e80d0fbe307c2e3ae002cc4fb93ad62d0351dea3df09eacfd11ed9db1bb
|
| 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 logRelease files / coinlytics-0.1.0-py3-none-any.whl
| Download URL | coinlytics-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2880aeedbc6357b0e5dfbe87e2560fa78850e7d89b807116b82c14b8ec531dc2
|
|
BLAKE2b-256 checksum How to use checksums |
4441a247733ca9bbaf7df6e121182c120c3e50025d448ed3beff5babe1f244aa
|
| 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