# Щоденні ціни DEX OHLC та VWAP для токена з точними дробами

> Source: https://docs.blockvectra.com/uk/guides/dex-daily-prices/

## Що являє собою набір даних щоденних цін DEX

Набір даних DEX від BlockVectra індексує торговельну активність децентралізованих бірж та розраховує агреговані щоденні показники цін. Ендпоінт щоденних цін DEX (`getDexPrices`) надає щоденну середньозважену за обсягом ціну (VWAP), цінові індикатори (поля `first_price`, `last_price`, `min_price` та `max_price`) і показники обсягу для вказаного токена за заданий діапазон дат.

Доступність цього набору даних різниться між мережами; перелік мереж, що надають цей набір даних, визначається на сторінці [Підтримувані мережі](https://docs.blockvectra.com/en/chains/).

Цей ендпоінт не підтримує пагінацію: всі відповідні щоденні записи в межах запитаного діапазону дат повертаються безпосередньо в полі `data`, а поле `next_cursor` ніколи не повертається. Якщо вашому застосунку потрібні детальні транзакції обміну (swaps), а не щоденні агреговані дані, використовуйте `GET /{chain}/dex/swaps` (дивіться [довідник Data API](https://docs.blockvectra.com/en/api/data/)).

## Параметри запиту та обмеження

Маршрут ендпоінта — `GET https://api.blockvectra.com/v1/data/{chain}/dex/prices`. Усі запити вимагають автентифікації шляхом передачі вашого API key у заголовку `x-api-key`.

Ендпоінт приймає такі параметри запиту:

| Параметр | Розташування | Тип        | Обов'язковий | Опис                                                                                              |
| -------- | ------------ | ---------- | ------------ | ------------------------------------------------------------------------------------------------- |
| `chain`  | path         | рядок      | Так          | Ідентифікатор мережі, наприклад `robinhood_mainnet`                                               |
| `token`  | query        | рядок      | Так          | 20-байтна адреса базового токена, префікс `0x` необов'язковий, будь-який регістр                  |
| `quote`  | query        | рядок      | Ні           | Необов'язкова 20-байтна адреса котирувального токена для обмеження запиту однією парою base/quote |
| `from`   | query        | рядок дати | Так          | Початкова дата за UTC, включно, `YYYY-MM-DD`                                                      |
| `to`     | query        | рядок дати | Так          | Кінцева дата за UTC, включно, `YYYY-MM-DD`. Різниця `to - from` має бути `<= 90` днів             |

### Обмеження та коди помилок

Коли запит порушує обмеження, API повертає структуроване тіло помилки `{"error":{"code","message"}}`:

* **HTTP 400 (`bad_request`)**: Відсутні обов'язкові параметри запиту (`token`, `from` або `to`), недійсний синтаксис адреси `token`/`quote`, недійсні календарні дати `YYYY-MM-DD` або `from` пізніше за `to`.
* **HTTP 409 (`span_exceeded`)**: Різниця `to - from` перевищує 90 днів.
* **HTTP 404 (`unknown_chain`)**: `{chain}` не є мережею зі списку, повернутого `GET /chains`.
* **HTTP 422 (`no_coverage`)**: Мережа не підтримує набір даних `dex_prices`.
* **HTTP 503 (`unavailable`)**: Сервіс тимчасово недоступний; повторіть спробу відповідно до заголовка `Retry-After`.

## Приклади запитів

У наведених нижче прикладах запитуються щоденні ціни DEX для базового токена за вересень 2026 року:

**cURL**

```bash
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/dex/prices?token=0x1Cdad396DB64BDa184d5182A97Dd9B3C62100b7D&from=2026-09-01&to=2026-09-30" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```


  **TypeScript**

```ts
const url = new URL("https://api.blockvectra.com/v1/data/robinhood_mainnet/dex/prices");
url.searchParams.set("token", "0x1Cdad396DB64BDa184d5182A97Dd9B3C62100b7D");
url.searchParams.set("from", "2026-09-01");
url.searchParams.set("to", "2026-09-30");

const res = await fetch(url, {
  headers: {
    "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
  },
});

if (!res.ok) {
  throw new Error(`Request failed with status ${res.status}`);
}

const body = await res.json();
console.log(body);
```


  **Python**

```python
import os
import requests

res = requests.get(
    "https://api.blockvectra.com/v1/data/robinhood_mainnet/dex/prices",
    params={
        "token": "0x1Cdad396DB64BDa184d5182A97Dd9B3C62100b7D",
        "from": "2026-09-01",
        "to": "2026-09-30",
    },
    headers={
        "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
    },
)
res.raise_for_status()
print(res.json())
```


## Детальний довідник полів

Кожен запис у масиві `data` представляє агреговані щоденні показники DEX для пари токенів за відповідну дату UTC:

### Базовий та котирувальний активи

* `day` (рядок): дата UTC у форматі `YYYY-MM-DD`.
* `token` (рядок): 20-байтна адреса базового токена в шістнадцятковому форматі в нижньому регістрі з префіксом `0x`.
* `token_symbol` (рядок або `null`): символ базового токена.
* `token_name` (рядок або `null`): відображувана назва базового токена.
* `quote_token` (рядок): адреса котирувального активу. Нульова адреса (`0x0000000000000000000000000000000000000000`) позначає нативний ETH як котирувальний актив.
* `quote_symbol` (рядок або `null`): символ котирувального активу (`"ETH"`, якщо `quote_token` — нульова адреса).
* `quote_name` (рядок або `null`): відображувана назва котирувального активу (`"Ether"`, якщо `quote_token` — нульова адреса).
* `base_decimals` (ціле число або `null`): кількість десяткових знаків базового токена (0–255).
* `quote_decimals` (ціле число або `null`): кількість десяткових знаків котирувального активу (`18`, якщо `quote_token` — нульова адреса).

### Обсяг і кількість угод

* `swap_count` (ціле число): кількість операцій обміну (swap) для цього рядка.
* `base_volume_raw` (рядок): атомарний обсяг базового токена у вигляді десяткового рядка беззнакового цілого числа (`UInt256String`).
* `quote_volume_raw` (рядок): атомарний обсяг котирувального токена у вигляді десяткового рядка беззнакового цілого числа (`UInt256String`).
* `base_volume` (рядок або `null`): зручний для читання людиною обсяг базового токена, масштабований за `base_decimals`, у форматі `DecimalString`; `null`, якщо `base_decimals` невідомо.
* `quote_volume` (рядок або `null`): обсяг котирувального активу, масштабований за кількістю десяткових знаків, у форматі `DecimalString`, або `null`.

### Цінові індикатори та VWAP

* `vwap` (рядок або `null`): середньозважена за обсягом ціна у форматі `DecimalString`, або `null`.
* `first_price` (рядок або `null`): індикатор початкової ціни (першої ціни) у форматі `DecimalString`, або `null`.
* `last_price` (рядок або `null`): індикатор кінцевої ціни (останньої ціни) у форматі `DecimalString`, або `null`.
* `min_price` (рядок або `null`): індикатор мінімальної ціни у форматі `DecimalString`, або `null`.
* `max_price` (рядок або `null`): індикатор максимальної ціни у форматі `DecimalString`, або `null`.

### Поля точних дробів

* `first_price_numerator` / `first_price_denominator` (рядок): точні цілочисельні чисельник і знаменник для `first_price` (`UInt256String`).
* `last_price_numerator` / `last_price_denominator` (рядок): точні цілочисельні чисельник і знаменник для `last_price` (`UInt256String`).
* `min_price_numerator` / `min_price_denominator` (рядок): точні цілочисельні чисельник і знаменник для `min_price` (`UInt256String`).
* `max_price_numerator` / `max_price_denominator` (рядок): точні цілочисельні чисельник і знаменник для `max_price` (`UInt256String`).
* `refreshed_at` (рядок): мітка часу оновлення для цього рядка (UTC-мітка часу за стандартом ISO-8601).

### Метадані відповіді (`meta`)

* `chain`: ідентифікатор мережі.
* `chain_slug`: канонічний ідентифікатор мережі у верхньому регістрі (slug).
* `chain_external_id`: ідентифікатор мережі у форматі CAIP-2.
* `as_of_block`: найновіший повністю записаний блок мережі (повідомляється цим набором даних, не зіставляється з параметрами запиту).
* `coverage`: класифікація покриття (для цього ендпоінта повідомляється `"full"`).
* `refreshed_at`: мітка часу оновлення метаданих. Може бути `null`: значення `null` означає, що час оновлення цих даних невідомий і їх слід вважати застарілими; ендпоінти на основі блоків завжди повертають значення.

## Чому ціни використовують точні чисельники та знаменники

Стандартні числа в JSON покладаються на числа з рухомою комою подвійної точності IEEE-754, які мають обмеження точності:

1. **Усічення чисел з рухомою комою та похибка накопичення**: значення Float64 забезпечують лише 53 біти точності, а ділення кількостей токенів призводить до похибки округлення, яка накопичується під час обчислень.
2. **Безпека передавання**: представлення значень у вигляді десяткових рядків (`UInt256String`) гарантує, що числа передаються через HTTP без втрати точності в парсерах JSON.

Надаючи точні цілочисельні чисельник і знаменник для цінових індикаторів, BlockVectra забезпечує можливість точних математичних розрахунків без перетворення в числа з рухомою комою.

### Обробка точних дробів у TypeScript (BigInt)

У TypeScript ви можете використовувати нативний `BigInt` для перехресного множення при порівнянні та перетворення в числа з фіксованою комою без конвертації у числа з рухомою комою:

```ts
interface DexDailyPrice {
  first_price_numerator: string;
  first_price_denominator: string;
  last_price_numerator: string;
  last_price_denominator: string;
}

// 1. Ratio comparison without floating-point conversion: check if close price is higher than open price
// a / b > c / d  is equivalent to  a * d > c * b
export function isPriceUp(row: DexDailyPrice): boolean {
  const openNum = BigInt(row.first_price_numerator);
  const openDen = BigInt(row.first_price_denominator);
  const closeNum = BigInt(row.last_price_numerator);
  const closeDen = BigInt(row.last_price_denominator);

  return closeNum * openDen > openNum * closeDen;
}

// 2. Convert fraction to a fixed-point decimal string with arbitrary scale (without floating-point loss)
export function fractionToFixedString(
  numeratorStr: string,
  denominatorStr: string,
  decimals = 18
): string {
  const num = BigInt(numeratorStr);
  const den = BigInt(denominatorStr);
  if (decimals === 0) {
    return (num / den).toString();
  }
  const scaleFactor = 10n ** BigInt(decimals);

  const scaled = (num * scaleFactor) / den;
  const intPart = scaled / scaleFactor;
  const remainder = scaled % scaleFactor;
  const fracPart = remainder.toString().padStart(decimals, "0");

  return `${intPart}.${fracPart}`;
}
```

### Обробка точних дробів у Python

Python надає модулі стандартної бібліотеки, створені спеціально для обчислень із раціональними та десятковими числами: `fractions.Fraction` та `decimal.Decimal`.

```python
from decimal import Decimal, getcontext
from fractions import Fraction

# 1. Exact rational calculations with fractions.Fraction
open_price = Fraction(
    int(row["first_price_numerator"]),
    int(row["first_price_denominator"])
)
close_price = Fraction(
    int(row["last_price_numerator"]),
    int(row["last_price_denominator"])
)

# Exact price delta without floating-point rounding error
price_delta = close_price - open_price
print(f"Price delta (fraction): {price_delta}")

if open_price != 0:
    percentage_change = (price_delta / open_price) * 100
    print(f"Percentage change: {float(percentage_change):.4f}%")

# 2. Arbitrary-precision decimal arithmetic with decimal.Decimal
getcontext().prec = 50

if int(row["first_price_denominator"]) != 0:
    open_decimal = Decimal(row["first_price_numerator"]) / Decimal(row["first_price_denominator"])
    print(f"High-precision open: {open_decimal}")
```

## Бекфіл щоденних цін за один рік

Щоб виконати бекфіл даних за один рік (365 днів) у межах 90-денного ліміту на один запит, розділіть повний діапазон дат на послідовні вікна тривалістю не більше 90 днів та надсилайте запити частинами:

```ts
interface DateSpan {
  from: string;
  to: string;
}

/**
 * Split a large date range into consecutive spans of at most maxDays (default: 90)
 */
export function splitDateRange(startDateStr: string, endDateStr: string, maxDays = 90): DateSpan[] {
  const spans: DateSpan[] = [];
  let currentStart = new Date(startDateStr);
  const end = new Date(endDateStr);

  while (currentStart <= end) {
    const chunkEnd = new Date(currentStart);
    chunkEnd.setUTCDate(chunkEnd.getUTCDate() + (maxDays - 1));
    const effectiveEnd = chunkEnd < end ? chunkEnd : end;

    spans.push({
      from: currentStart.toISOString().slice(0, 10),
      to: effectiveEnd.toISOString().slice(0, 10),
    });

    const nextStart = new Date(effectiveEnd);
    nextStart.setUTCDate(nextStart.getUTCDate() + 1);
    currentStart = nextStart;
  }

  return spans;
}

/**
 * Backfill token daily prices across multiple 90-day chunks
 */
export async function backfillTokenDailyPrices(
  chain: string,
  token: string,
  startDate: string,
  endDate: string,
  apiKey: string
) {
  const chunks = splitDateRange(startDate, endDate, 90);
  const allDailyPrices = [];

  for (const chunk of chunks) {
    const url = new URL(`https://api.blockvectra.com/v1/data/${chain}/dex/prices`);
    url.searchParams.set("token", token);
    url.searchParams.set("from", chunk.from);
    url.searchParams.set("to", chunk.to);

    const res = await fetch(url, {
      headers: { "x-api-key": apiKey },
    });

    if (!res.ok) {
      throw new Error(`Failed to fetch span ${chunk.from}..${chunk.to}: HTTP ${res.status}`);
    }

    const json = await res.json();
    allDailyPrices.push(...json.data);
  }

  return allDailyPrices;
}
```

## Розрахунок пропускної здатності та використання CU

Кожен ендпоінт Data API вимірює споживання в Compute Units (CU). Вага в CU за один виклик для `data.dex_prices` та розрахункове споживання для бекфілу токенів наведені нижче:

- **Вага методу:** `data.dex_prices` = 15 CU / виклик.
- **Поповнення щоденних цін за 1 рік для 200 токенів:** з максимальним інтервалом у 90 дн. на запит охоплення 365 дн. потребує 5 частин на токен, загалом 1,000 викликів. Загальне споживання становить 15,000 CU (приблизно <0.1% від ліміту циклу безкоштовного плану), близько <$0.01 за прайс-листом.
- **Щоденне оновлення (оновлення 200 токенів один раз на день):** 200 викликів/день (3,000 CU/день), загалом приблизно 6,000 викликів за цикл у 30 дн. (90,000 CU, приблизно 0.3% від безкоштовної квоти), близько <$0.01/місяць за прайс-листом.

При збільшенні обсягу бекфілу або необхідності вищої кількості одночасних запитів здійсніть ончейн-поповнення на [сторінці білінгу](https://console.blockvectra.com/billing/) в консолі, щоб перейти на платний акаунт. Чинні тарифи та конвертацію одиниць дивіться на [сторінці цін](https://blockvectra.com/en/pricing/).

## Наступні кроки

* [Перегляньте каталог наборів даних](https://blockvectra.com/en/data/), щоб ознайомитися з усіма наборами даних, які індексує BlockVectra.
* [Дізнайтеся про безкоштовний план і ціни](https://blockvectra.com/en/pricing/#free), щоб перевірити, що включено у ваш акаунт.
* [Увійдіть до консолі](https://console.blockvectra.com/login/?next=%2Fkeys%2F), щоб створити API key.
