# Дневные цены DEX OHLC и VWAP для токена с точными дробями

> Source: https://docs.blockvectra.com/ru/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` никогда не возвращается. Если вашему приложению требуются детальные транзакции свопов, а не дневные агрегаты, используйте `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`  | путь         | string      | Да           | Идентификатор сети, например `robinhood_mainnet`                                               |
| `token`  | query        | string      | Да           | 20-байтовый адрес базового токена, `0x` необязателен, регистр не имеет значения                |
| `quote`  | query        | string      | Нет          | Необязательный 20-байтовый адрес котируемого токена для ограничения одной парой база/котировка |
| `from`   | query        | date string | Да           | Начальная дата UTC, включительно, `YYYY-MM-DD`                                                 |
| `to`     | query        | date string | Да           | Конечная дата 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` (string): дата UTC в формате `YYYY-MM-DD`.
* `token` (string): 20-байтовый адрес базового токена в шестнадцатеричном формате нижнего регистра с префиксом `0x`.
* `token_symbol` (string или `null`): символ базового токена.
* `token_name` (string или `null`): отображаемое название базового токена.
* `quote_token` (string): адрес котируемого актива. Нулевой адрес (`0x0000000000000000000000000000000000000000`) обозначает нативный ETH в качестве котируемого актива.
* `quote_symbol` (string или `null`): символ котируемого актива (`"ETH"`, когда `quote_token` является нулевым адресом).
* `quote_name` (string или `null`): отображаемое название котируемого актива (`"Ether"`, когда `quote_token` является нулевым адресом).
* `base_decimals` (integer или `null`): количество десятичных знаков базового токена (0–255).
* `quote_decimals` (integer или `null`): количество десятичных знаков котируемого актива (`18`, когда `quote_token` является нулевым адресом).

### Объем и количество сделок

* `swap_count` (integer): количество свопов в этой строке.
* `base_volume_raw` (string): атомарный объем базового актива в виде десятичной строки беззнакового целого числа (`UInt256String`).
* `quote_volume_raw` (string): атомарный объем котируемого актива в виде десятичной строки беззнакового целого числа (`UInt256String`).
* `base_volume` (string или `null`): человекочитаемый объем базового токена, масштабированный по `base_decimals`, в виде `DecimalString`; `null`, если `base_decimals` неизвестно.
* `quote_volume` (string или `null`): объем котируемого актива, масштабированный по его десятичным знакам, в виде `DecimalString` или `null`.

### Индикаторы цен и VWAP

* `vwap` (string или `null`): средневзвешенная по объему цена в виде `DecimalString` или `null`.
* `first_price` (string или `null`): индикатор цены открытия (первой цены) в виде `DecimalString` или `null`.
* `last_price` (string или `null`): индикатор цены закрытия (последней цены) в виде `DecimalString` или `null`.
* `min_price` (string или `null`): индикатор минимальной цены в виде `DecimalString` или `null`.
* `max_price` (string или `null`): индикатор максимальной цены в виде `DecimalString` или `null`.

### Поля точных дробей

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

### Метаданные ответа (meta)

* `chain`: идентификатор сети.
* `chain_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/ru/pricing/).

## Следующие шаги

* [Изучите каталог наборов данных](https://blockvectra.com/ru/data/), чтобы увидеть все наборы данных, индексируемые BlockVectra.
* [Ознакомьтесь с бесплатным тарифом и ценами](https://blockvectra.com/ru/pricing/#free), чтобы узнать, что включено в ваш аккаунт.
* [Войдите в консоль](https://console.blockvectra.com/login/?next=%2Fkeys%2F), чтобы создать API key.
