Metadata-Version: 2.4
Name: coinlytics
Version: 0.1.0
Summary: Analytics and market-data networking layer for the crypto tracker ecosystem
Author: Jacob Kanfer
License-Expression: MIT OR Apache-2.0
Project-URL: Homepage, https://pypi.org/project/coinlytics
Project-URL: Repository, https://github.com/Technical-1/coinlytics-py
Project-URL: Issues, https://github.com/Technical-1/coinlytics-py/issues
Project-URL: Documentation, https://github.com/Technical-1/coinlytics-py#readme
Keywords: crypto,cryptocurrency,portfolio,analytics,market-data,coingecko,rebalancing,cost-basis,staking
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE-MIT
License-File: LICENSE-APACHE
Requires-Dist: coinbasis<0.2,>=0.1
Requires-Dist: requests<3,>=2.28
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Dynamic: license-file

# coinlytics

[![PyPI version](https://img.shields.io/pypi/v/coinlytics)](https://pypi.org/project/coinlytics/)
[![Python versions](https://img.shields.io/pypi/pyversions/coinlytics)](https://pypi.org/project/coinlytics/)
[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](https://github.com/Technical-1/coinlytics-py#license)
[![CI](https://github.com/Technical-1/coinlytics-py/actions/workflows/ci.yml/badge.svg)](https://github.com/Technical-1/coinlytics-py/actions/workflows/ci.yml)

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-After` header support; TTL on-disk JSON cache (atomic writes); offline last-good fallback with `PriceBook.stale` flag.
- **`MockClient`** — satisfies the same `PriceSource` protocol as the real client; deterministic, no network; a `fail_ids` set exercises error-handling paths without mocking `requests`.
- **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 `Sell` to a throwaway `coinbasis.Portfolio` and 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 `Snapshot` dataclass; delegates volatility, Sharpe ratio, max drawdown, and cumulative return to `coinbasis.stats`.
- **DefiLlama staking APY** — fetches pool yields for a list of symbols; selects the highest-TVL exact-match pool per symbol.
- **News + sentiment** — `fetch_rss` and `fetch_cryptopanic` with a DOCTYPE entity-expansion guard (stdlib only, no `defusedxml`); 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

```bash
pip install coinlytics
```

For local development against an unpublished `coinbasis`:

```bash
pip install -e ../coinbasis-py   # local coinbasis first
pip install -e ".[dev]"          # coinlytics + pytest + ruff
```

### Fetch current prices (keyless, no API key needed)

```python
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)

```python
cfg = CoinGeckoConfig(
    api_key="your-demo-key",
    plan="demo",
    cache_dir="~/.cache/coinlytics",
    cache_ttl=120,
)
client = CoinGeckoClient(cfg)
```

### Tax-aware rebalancing

```python
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

```python
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

```python
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/`](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

```bash
# 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](https://github.com/Technical-1)
