Analytics and market-data networking layer for the crypto tracker ecosystem
Project description
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
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 coinlytics-0.1.0.tar.gz.
File metadata
- Download URL: coinlytics-0.1.0.tar.gz
- Upload date:
- Size: 56.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46751a6f85e591758a91453f90a5547e3dfd9fbce047083f370cf40d38b1853f
|
|
| MD5 |
3a2a0239d78ca64c69a062a1e00b3a8c
|
|
| BLAKE2b-256 |
53161e80d0fbe307c2e3ae002cc4fb93ad62d0351dea3df09eacfd11ed9db1bb
|
Provenance
The following attestation bundles were made for coinlytics-0.1.0.tar.gz:
Publisher:
publish.yml on Technical-1/coinlytics-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coinlytics-0.1.0.tar.gz -
Subject digest:
46751a6f85e591758a91453f90a5547e3dfd9fbce047083f370cf40d38b1853f - Sigstore transparency entry: 1722626645
- Sigstore integration time:
-
Permalink:
Technical-1/coinlytics-py@e9a3341d27a40187008b7ac71c2e3725aae92819 -
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@e9a3341d27a40187008b7ac71c2e3725aae92819 -
Trigger Event:
release
-
Statement type:
File details
Details for the file coinlytics-0.1.0-py3-none-any.whl.
File metadata
- Download URL: coinlytics-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.4 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 |
2880aeedbc6357b0e5dfbe87e2560fa78850e7d89b807116b82c14b8ec531dc2
|
|
| MD5 |
616bf3fa2604d7c2f8ca99b45cd37cc6
|
|
| BLAKE2b-256 |
4441a247733ca9bbaf7df6e121182c120c3e50025d448ed3beff5babe1f244aa
|
Provenance
The following attestation bundles were made for coinlytics-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Technical-1/coinlytics-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coinlytics-0.1.0-py3-none-any.whl -
Subject digest:
2880aeedbc6357b0e5dfbe87e2560fa78850e7d89b807116b82c14b8ec531dc2 - Sigstore transparency entry: 1722626769
- Sigstore integration time:
-
Permalink:
Technical-1/coinlytics-py@e9a3341d27a40187008b7ac71c2e3725aae92819 -
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@e9a3341d27a40187008b7ac71c2e3725aae92819 -
Trigger Event:
release
-
Statement type: