npm package: @hodlbase/client
Description: Official TypeScript/JavaScript client for the HodlBase Bitcoin treasury data API
Latest version: 0.4.0

# @hodlbase/client

Official TypeScript/JavaScript client for the [HodlBase](https://hodlbase.io) API — structured financial data for Bitcoin treasury companies and their preferred securities.

- Zero dependencies — uses native `fetch` (Node.js 18+, all modern browsers)
- Full TypeScript support with strict types
- Automatic pagination for list and timeseries endpoints
- ESM and CommonJS

## Installation

```bash
npm install @hodlbase/client
```

## Quick Start

```typescript
import { HodlbaseClient } from "@hodlbase/client";

const client = new HodlbaseClient({ apiKey: "your-api-key" });

// List all Bitcoin treasury companies
const { data: companies } = await client.companies.list();

// Get live BTC holdings and mNAV for Strategy (MSTR)
const live = await client.companies.getLive("MSTR", "fundamental_metrics");
```

## Resources

The client exposes two resource namespaces: `companies` and `securities`.

### Companies

Bitcoin treasury companies — publicly traded companies that hold Bitcoin on their balance sheet.

```typescript
// List all companies (auto-paginated)
const { data, metadata } = await client.companies.list();

// Get company details
const company = await client.companies.get("MSTR");

// Get available metrics
const metrics = await client.companies.metrics();

// Get latest values for a metric subgroup
const live = await client.companies.getLive("MSTR", "fundamental_metrics");

// Get latest value for a single metric
const metric = await client.companies.getMetricLive(
  "MSTR",
  "fundamental_metrics",
  "btc_holdings",
);

// Get historical timeseries (auto-paginated)
const ts = await client.companies.getTimeseries("MSTR", "fundamental_metrics", {
  granularity: "1d",
  days: 30,
});

// Get timeseries for a single metric
const mnav = await client.companies.getMetricTimeseries(
  "MSTR",
  "fundamental_metrics",
  "mnav",
  { granularity: "1d", days: 90 },
);
```

**Company metric subgroups:**

| Subgroup | Key metrics |
|---|---|
| `fundamental_metrics` | BTC holdings, BTC reserve (USD), mNAV, Bitcoin per share, enterprise value |
| `common_equity` | Common stock price, market cap, shares outstanding (basic & fully diluted), shares per BTC |
| `capital_structure_leverage` | Debt outstanding, amplification, net leverage, preferred amplification |
| `digital_credit_aggregates` | Preferred notional, preferred market cap, USD cash reserve, annual dividends |
| `dividends_coverage` | Annual preferred dividends, convertible bond coupons, BTC years & USD months of dividend coverage |
| `volatility` | BTC 7-day and 30-day historical volatility |
| `other` | Bitcoins bought YTD, capital raised YTD |

### Securities

Preferred securities issued by Bitcoin treasury companies to raise capital (e.g. STRK, STRF).

```typescript
// List all preferred securities (with optional filters)
const { data } = await client.securities.list({ companyTicker: "MSTR" });

// Get security details
const strk = await client.securities.get("STRK");

// Get live data
const live = await client.securities.getLive("STRK", "fundamental_metrics");

// Get historical timeseries
const ts = await client.securities.getTimeseries("STRK", "credit_quality", {
  granularity: "1d",
  days: 30,
});
```

**Security metric subgroups:**

| Subgroup | Key metrics |
|---|---|
| `fundamental_metrics` | Close price, effective yield, market cap, notional value, dividend rate, shares outstanding |
| `credit_quality` | Bitcoin rating (pure & USD-adjusted), higher seniority notional |
| `atm_capital_metrics` | Cumulative capital raised, cumulative ATM capital, total dividends paid, total Bitcoin accumulated |
| `volume_metrics` | Daily & 30-day volume, currency volume, volume above par |
| `credit_spreads` | Yield spread vs AAA, SOFR, US 10Y |
| `advanced_metrics` | Macaulay duration, 90-day BTC price correlation, 30-day historical volatility |

## Timeseries Options

All timeseries methods accept an options object:

```typescript
interface TimeseriesOptions {
  start?: string;       // ISO date string, e.g. "2024-01-01"
  end?: string;         // ISO date string
  days?: number;        // Number of historical days
  granularity?: Granularity; // "1min" | "1h" | "2h" | "4h" | "12h" | "1d" | "1w" | "1m"
  maxItems?: number;    // Max total data points to return (sequential methods only)
  concurrency?: number; // Parallel chunks for Fast methods (default: 4)
}
```

## Streaming Pages

For large datasets, iterate one page at a time instead of loading everything into memory:

```typescript
for await (const page of client.companies.getTimeseriesPages("MSTR", "fundamental_metrics")) {
  console.log(`Got ${page.data.length} points`);
  // process page.data incrementally
}
```

## Fast Fetching (Parallel)

For large datasets, use the `Fast` variants to fetch timeseries data in parallel. These methods use a **probe-then-parallel** strategy:

1. A lightweight probe request (`limit=1`) discovers the full date range and whether the data spans multiple pages
2. If multi-page: the range is split into concurrent chunks (default: 4), fetched in parallel, and merged
3. If single-page: falls back to a normal sequential fetch (no overhead beyond the probe)

```typescript
// Single metric — parallel
const mnav = await client.companies.getMetricTimeseriesFast(
  "MSTR",
  "fundamental_metrics",
  "mnav",
  { granularity: "1h", days: 365 },
);

// Subgroup timeseries — parallel
const ts = await client.companies.getTimeseriesFast("MSTR", "fundamental_metrics", {
  granularity: "1h",
  days: 90,
});

// Custom concurrency (default is 4)
const ts6 = await client.securities.getMetricTimeseriesFast(
  "STRK",
  "credit_spreads",
  "cs_aaa",
  { granularity: "1h", days: 90, concurrency: 6 },
);
```

The `Fast` methods return the exact same types (`TimeseriesData` / `SingleMetricTimeseriesData`) as their sequential counterparts — they're drop-in replacements.

### Performance

Benchmarked against the production API (7 runs, trimmed mean, 95% CI):

| Scenario | Points | Normal | Fast | Speedup |
|---|---|---|---|---|
| 1D · 30 days | 30 | 59ms ± 4ms | 120ms ± 6ms | 0.49x |
| 1D · 365 days | 365 | 72ms ± 8ms | 135ms ± 13ms | 0.53x |
| **1H · 90 days** | **2,160** | **434ms ± 10ms** | **294ms ± 50ms** | **1.48x** |
| **1H · 365 days** | **8,755** | **1,649ms ± 60ms** | **668ms ± 38ms** | **2.47x** |

**When to use which:**

| Method | Best for |
|---|---|
| `getTimeseries` / `getMetricTimeseries` | Small-to-medium requests (< 1,000 data points). Daily granularity, short date ranges. |
| `getTimeseriesFast` / `getMetricTimeseriesFast` | Large requests (1,000+ data points). Hourly or finer granularity, long date ranges. Up to **2.5x faster**. |

> Run `HODLBASE_API_KEY=your-key node benchmarks/benchmark.mjs` to reproduce these results.

## Error Handling

The client throws typed errors you can catch individually:

```typescript
import {
  HodlbaseError,
  AuthenticationError,
  RateLimitError,
  NotFoundError,
} from "@hodlbase/client";

try {
  const company = await client.companies.get("INVALID");
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log("Company not found");
  } else if (error instanceof AuthenticationError) {
    console.log("Invalid API key");
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited — retry after ${error.retryAfter}s`);
  } else if (error instanceof HodlbaseError) {
    console.log(`API error ${error.statusCode}: ${error.detail}`);
  }
}
```

**Error classes:** `AuthenticationError` (401), `ValidationError` (400), `ForbiddenError` (403), `NotFoundError` (404), `RateLimitError` (429), `HodlbaseError` (base)

## TypeScript

All types are exported for use in your code:

```typescript
import type {
  Company,
  CompanyDetail,
  Security,
  SecurityDetail,
  LiveData,
  MetricValue,
  TimeseriesData,
  TimeseriesPoint,
  Granularity,
  CompanySubgroup,
  SecuritySubgroup,
} from "@hodlbase/client";
```

## Development

The dev API sits behind HTTP Basic Auth. Use `baseUrl` and `extraHeaders` to connect:

```typescript
import { HodlbaseClient } from "@hodlbase/client";

const client = new HodlbaseClient({
  apiKey: "your-api-key",
  baseUrl: "https://dev-api.hodlbase.io/v1",
  extraHeaders: {
    Authorization: `Basic ${btoa("username:password")}`,
  },
});
```

Replace `username` and `password` with your dev credentials.

## License

MIT