Щоденні ціни 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 ніколи не повертається. Якщо вашому застосунку потрібні детальні транзакції обміну (swaps), а не щоденні агреговані дані, використовуйте GET /{chain}/dex/swaps (дивіться довідник Data API).

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

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

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

ПараметрРозташуванняТипОбов'язковийОпис
chainpathрядокТакІдентифікатор мережі, наприклад robinhood_mainnet
tokenqueryрядокТак20-байтна адреса базового токена, префікс 0x необов'язковий, будь-який регістр
quotequeryрядокНіНеобов'язкова 20-байтна адреса котирувального токена для обмеження запиту однією парою base/quote
fromqueryрядок датиТакПочаткова дата за UTC, включно, YYYY-MM-DD
toqueryрядок датиТакКінцева дата за 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 (рядок): дата 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 для перехресного множення при порівнянні та перетворення в числа з фіксованою комою без конвертації у числа з рухомою комою:

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/місяць за прайс-листом.

При збільшенні обсягу бекфілу або необхідності вищої кількості одночасних запитів здійсніть ончейн-поповнення на сторінці білінгу в консолі, щоб перейти на платний акаунт. Чинні тарифи та конвертацію одиниць дивіться на сторінці цін.

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

Востаннє оновлено:

На цій сторінці