# Data & Methodology

Every number the API serves is a **total return** computed from **adjusted** end-of-day
prices — the same figures published in the [Fruit Stand fund-returns dataset on the Snowflake
Marketplace](https://fruitstand.dev/datasets/fund-returns). This page explains exactly how
they're derived so you can trust them in production.

## Total return, from adjusted prices

Returns are calculated from **adjusted closing prices**, not raw closes. The adjusted close
accounts for stock splits and dividend/distribution reinvestment, so every return reflects
**total return** — price change *plus* reinvested income — not price change alone.

## Cumulative vs. annualized

A return is either **cumulative** (the raw growth over the span) or **annualized** (a compound
annual growth rate, CAGR), decided by the length of the period:

**Cumulative** — used for every period **under 2 years** and for all calendar-year returns:

```
cumulative_return = (end_adjusted_close / base_adjusted_close) - 1
```

**Annualized (CAGR)** — used for periods of **2 years and longer**:

```
annualized_return = (end_adjusted_close / base_adjusted_close) ^ (365.25 / days_between) - 1
```

One guard rail: **a span under 365 days is never annualized.** If a normally-annualized period
happens to resolve to less than a year of actual data (a young fund, say), it falls back to the
cumulative formula so short spans aren't exaggerated.

| Period | Basis |
|---|---|
| `return_1d` … `return_1y`, `return_ytd` | Cumulative |
| `return_2y` … `return_20y` | Annualized (CAGR) |
| `return_earliest_available` (since inception) | Annualized |
| `calendar_year_return` | Always cumulative |

## Units

All returns are **decimal fractions**, not percentages: `0.0523` means **+5.23%**, `-0.10`
means **−10%**. Values carry up to **6 decimal places**. Multiply by 100 for display.

A return is `null` when a fund lacks enough price history to cover that period (e.g. a 3-year-old
fund has no `return_5y`).

## Calendar-year returns

One row per fund per calendar year, always cumulative:

- **`base_date`** — the last trading day of the *prior* year (or the fund's first trading day for
  its inception year).
- **`end_date`** — the last trading day of that calendar year (or the latest available day for the
  current, in-progress year).
- The **current year** row is therefore a year-to-date figure; the **inception year** row is a
  partial return from the fund's first trading day to year-end.

## The as-of / trading-day rule

Trailing returns are stored per fund per trading day. When you request an `as_of` date that isn't
a trading day (a weekend or holiday), the API **snaps back to the nearest prior trading day** and
returns that row — the response's `as_of_date` tells you the actual day used. Omit `as_of` to get
each fund's latest available row.

## Price-gap fallback (thinly-traded funds)

For illiquid funds with gaps in their price history, a period still returns a value by falling
back to the **nearest earlier date that has an actual price**. The trade-off: for such funds the
base price may be slightly older than the period label implies (e.g. a "1-month" base that is
really 5 weeks back). Liquid funds are unaffected.

## Data-quality checks

The pipeline validates every refresh before it publishes:

- Every closing and adjusted-closing price must be **strictly positive**.
- No return may mathematically fall **below −100%**.
- 1-day and 1-week returns beyond **±50%** are flagged for investigation.

See [Coverage & limitations](/coverage) for what's in scope, and the
[Field reference](/fields) for every column and its units.
