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

Запрашивайте дневные цены DEX OHLC и VWAP из Data API, обрабатывайте точные рациональные дроби в TypeScript и Python и эффективно дозагружайте исторические данные.

Что представляет собой набор данных дневных цен DEX

Набор данных DEX BlockVectra индексирует торговую активность на децентрализованных биржах и вычисляет агрегированные дневные метрики цен. Эндпоинт дневных цен DEX (getDexPrices) предоставляет дневную средневзвешенную по объему цену (VWAP), ценовые индикаторы (поля first_price, last_price, min_price и max_price) и показатели объема для указанного токена за заданный диапазон дат.

Доступность этого набора данных различается по сетям; сети, поддерживающие данный набор данных, указаны на странице Поддерживаемые сети.

Этот эндпоинт не поддерживает пагинацию: все соответствующие дневные записи за запрошенный диапазон дат возвращаются напрямую в data, а поле next_cursor никогда не возвращается. Если вашему приложению требуются детальные транзакции свопов, а не дневные агрегаты, используйте GET /{chain}/dex/swaps (см. справочник Data API).

Параметры запроса и ограничения

Маршрут эндпоинта — GET https://api.blockvectra.com/v1/data/{chain}/dex/prices. Все запросы требуют аутентификации путем передачи API key в заголовке x-api-key.

Эндпоинт принимает следующие параметры запроса:

ПараметрРасположениеТипОбязательныйОписание
chainпутьstringДаИдентификатор сети, например robinhood_mainnet
tokenquerystringДа20-байтовый адрес базового токена, 0x необязателен, регистр не имеет значения
quotequerystringНетНеобязательный 20-байтовый адрес котируемого токена для ограничения одной парой база/котировка
fromquerydate stringДаНачальная дата UTC, включительно, YYYY-MM-DD
toquerydate 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 -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"

Подробный справочник полей

Каждый элемент массива 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 для перекрестного умножения при сравнении и преобразования в числа с фиксированной запятой без использования чисел с плавающей запятой:

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.

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 дней и отправляйте запросы по частям:

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 / call
  • Ретроспективная загрузка ежедневных цен за 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/месяц по прайс-листу.

При масштабировании объема выгрузки или необходимости более высокого параллелизма запросов выполните ончейн-пополнение в консоли на странице биллинга, чтобы перейти на платный аккаунт. Актуальные тарифы и конвертацию расчетных единиц см. на странице Цены.

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

Последнее обновление:

На этой странице