boxtime 3.4.0
Cointime economics framework implementation for the Ergo blockchain.
License: MIT
Requires Python: >=3.10

# boxtime

Cointime economics framework implementation for the Ergo blockchain.

## Description

Boxtime is a Python library that implements the [Cointime Economics](https://insights.glassnode.com/introducing-cointime-economics/) framework for Ergo. It reads pre-computed coinblocks created, destroyed, and stored — plus daily ERG/USD prices — from a database populated by the boxtime indexer.

Two connection paths are supported:

1. **Default (Supabase)** — uses the hosted Supabase project via PostgREST with an embedded publishable API key. No setup needed.
2. **Custom (Postgres)** — connect directly to your own Postgres instance via `asyncpg`.

## Installation

```bash
pip install boxtime
```

## Prerequisites

- **Python 3.10+**

## Quickstart

### Default (Supabase — no setup needed)

```python
from boxtime import Cointime

ct = Cointime()  # uses the hosted Supabase database

# Coinblocks created at a single height (= circulating supply in nanoERGs)
cbc = ct.coinblocks_created(500_000)

# Coinblocks destroyed at a single height
cbd = ct.coinblocks_destroyed(500_000)

# Coinblocks stored (CBC - CBD)
cbs = ct.coinblocks_stored(500_000)

# Totals over a range
total_cbc = ct.total_coinblocks_created(500_000, 500_100)
total_cbd = ct.total_coinblocks_destroyed(500_000, 500_100)
total_cbs = ct.total_coinblocks_stored(500_000, 500_100)
```

### Custom Postgres

```python
ct = Cointime(database_url="postgresql://user:pass@localhost:5432/boxtime")
cbc = ct.coinblocks_created(500_000)
```

### Price data

```python
import datetime

ct = Cointime()

# Daily ERG/USD price
price = ct.get_price(datetime.date(2024, 6, 15))

# Price range
prices = ct.get_price_range(datetime.date(2024, 1, 1), datetime.date(2024, 12, 31))
for date, usd in prices:
    print(date, usd)
```

### Time series data (for plotting)

The `get_coinblocks_*` methods return a `CointimeSeries` object optimized for plotting:

```python
# All blocks in range (no prices)
series = ct.get_coinblocks_created(500_000, 510_000)

# Access as parallel arrays
print(series.heights)      # [500000, 500001, ...]
print(series.timestamps)   # [1704067200000, ...]
print(series.values)       # [cbc values]

# Or iterate as data points
for dp in series:
    print(dp.height, dp.timestamp, dp.value)

# Convert timestamps to dates for plotting
import matplotlib.pyplot as plt
plt.plot(series.dates, series.values)
```

### Price-enriched time series

```python
# One data point per day with price enrichment
series = ct.get_coinblocks_created(500_000, 510_000, with_price=True)

for dp in series:
    print(f"Height {dp.height}: {dp.value} nanoERGs @ ${dp.price:.2f}")

# Clean up when done
ct.close()
```

## API Overview

### `Cointime(database_url=None, *, supabase_url=..., supabase_key=...)`

| Parameter | Default | Description |
| --- | --- | --- |
| `database_url` | `None` | If set, use direct Postgres via asyncpg |
| `supabase_url` | Hosted URL | Supabase project URL (default path) |
| `supabase_key` | Publishable key | Supabase API key (safe to embed) |

**Primitive metrics** (single height):
- `coinblocks_created(height)` — circulating supply at height
- `coinblocks_destroyed(height)` — sum of `input.value × lifespan` for all inputs
- `coinblocks_stored(height)` — CBC − CBD

**Aggregate metrics** (height range, inclusive):
- `total_coinblocks_created(start_height, end_height)`
- `total_coinblocks_destroyed(start_height, end_height)`
- `total_coinblocks_stored(start_height, end_height)`

**Price methods**:
- `get_price(date)` — daily ERG/USD price (`datetime.date`)
- `get_price_range(start_date, end_date)` — `List[Tuple[date, float]]`

**Time series methods** (returns `CointimeSeries`):

All accept an optional `with_price=True` parameter to include daily prices.

- `get_coinblocks_created(start_height, end_height, *, with_price=False)`
- `get_coinblocks_destroyed(start_height, end_height, *, with_price=False)`
- `get_coinblocks_stored(start_height, end_height, *, with_price=False)`

**Cumulative time series** (absolute cumulative sums from genesis):

- `get_total_coinblocks_created(start_height, end_height, *, with_price=False)` — TCBC
- `get_total_coinblocks_destroyed(start_height, end_height, *, with_price=False)` — TCBD
- `get_total_coinblocks_stored(start_height, end_height, *, with_price=False)` — TCBS

**Utility**:
- `get_max_height()` — check data freshness
- `close()` — clean up connection pool / client

### `CointimeSeries`

Returned by time series methods. Optimized for plotting with parallel arrays:

| Attribute | Type | Description |
| --- | --- | --- |
| `heights` | `List[int]` | Block heights |
| `timestamps` | `List[int]` | Block timestamps (ms since epoch) |
| `values` | `List[int]` | Cointime values (nanoERG-blocks) |
| `prices` | `List[float] \| None` | ERG/USD prices (if `with_price=True`) |

**Methods**:
- `__iter__()` — iterate as `CointimeDataPoint` objects
- `__len__()` — number of data points
- `dates` — property returning `List[datetime.date]` for plotting

### `CointimeDataPoint`

Individual data point from iterating a `CointimeSeries`:

| Attribute | Type | Description |
| --- | --- | --- |
| `height` | `int` | Block height |
| `timestamp` | `int` | Block timestamp (ms since epoch) |
| `value` | `int` | Cointime value |
| `price` | `float \| None` | ERG/USD price (if available) |

## Development

```bash
# Install with test dependencies
pip install -e ".[test]"

# Run tests
pytest
```

## License

This project is licensed under the [MIT License](LICENSE).