OHLC dan VWAP DEX harian untuk token dengan pecahan eksak

Kueri harga OHLC dan VWAP DEX harian dari Data API, tangani pecahan rasional eksak di TypeScript dan Python, serta lakukan backfill data historis secara efisien.

Apa itu dataset harga DEX harian

Dataset DEX BlockVectra mengindeks aktivitas perdagangan bursa terdesentralisasi dan menghitung metrik harga harian agregat. Endpoint harga DEX harian (getDexPrices) menyediakan harga rata-rata tertimbang volume (VWAP) harian, indikator harga (bidang first_price, last_price, min_price, dan max_price), serta metrik volume untuk token tertentu selama rentang tanggal yang ditentukan.

Ketersediaan dataset ini berbeda antarjaringan; rantai yang menyediakan dataset ini mengikuti halaman Rantai yang Didukung.

Endpoint ini tidak menggunakan paginasi: semua baris harian yang cocok dalam rentang tanggal permintaan dikembalikan langsung dalam data, dan next_cursor tidak pernah disertakan. Jika aplikasi Anda memerlukan transaksi swap individual alih-alih agregat harian, gunakan GET /{chain}/dex/swaps (lihat Referensi Data API).

Parameter permintaan dan batas

Rute endpoint adalah GET https://api.blockvectra.com/v1/data/{chain}/dex/prices. Semua permintaan memerlukan autentikasi dengan menyertakan API key Anda pada header x-api-key.

Endpoint ini menerima parameter kueri berikut:

ParameterLokasiTipeWajibDeskripsi
chainpathstringYaIdentitas rantai, misalnya robinhood_mainnet
tokenquerystringYaAlamat token dasar 20 byte, 0x opsional, huruf besar atau kecil
quotequerystringTidakAlamat token kuotasi 20 byte opsional untuk membatasi ke satu pasangan dasar/kuotasi
fromquerydate stringYaTanggal mulai UTC, inklusif, YYYY-MM-DD
toquerydate stringYaTanggal akhir UTC, inklusif, YYYY-MM-DD. to - from harus <= 90 hari

Batasan dan kode error

Jika permintaan melanggar batasan, API mengembalikan body error terstruktur {"error":{"code","message"}}:

  • HTTP 400 (bad_request): Parameter kueri wajib (token, from, atau to) tidak disertakan, sintaks alamat token/quote tidak valid, tanggal kalender YYYY-MM-DD tidak valid, atau from berada setelah to.
  • HTTP 409 (span_exceeded): to - from lebih dari 90 hari.
  • HTTP 404 (unknown_chain): {chain} bukan rantai yang tercantum dalam GET /chains.
  • HTTP 422 (no_coverage): Rantai tidak mendukung kapabilitas dataset dex_prices.
  • HTTP 503 (unavailable): Layanan sementara tidak tersedia; coba lagi sesuai header Retry-After.

Contoh permintaan

Contoh berikut mengueri harga DEX harian untuk token dasar sepanjang September 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"

Referensi bidang terperinci

Setiap entri dalam data mewakili metrik DEX harian agregat untuk pasangan token pada tanggal UTC tersebut:

Token dan aset kuotasi

  • day (string): Tanggal UTC dalam format YYYY-MM-DD.
  • token (string): Alamat token dasar 20 byte dalam heksadesimal huruf kecil dengan awalan 0x.
  • token_symbol (string atau null): Simbol token dasar.
  • token_name (string atau null): Nama tampilan token dasar.
  • quote_token (string): Alamat aset kuotasi. Alamat yang seluruhnya nol (0x0000000000000000000000000000000000000000) mewakili ETH native sebagai aset kuotasi.
  • quote_symbol (string atau null): Simbol aset kuotasi ("ETH" jika quote_token adalah alamat yang seluruhnya nol).
  • quote_name (string atau null): Nama tampilan aset kuotasi ("Ether" jika quote_token adalah alamat yang seluruhnya nol).
  • base_decimals (integer atau null): Jumlah desimal token dasar (0–255).
  • quote_decimals (integer atau null): Jumlah desimal aset kuotasi (18 jika quote_token adalah alamat yang seluruhnya nol).

Volume dan jumlah perdagangan

  • swap_count (integer): Jumlah swap untuk baris ini.
  • base_volume_raw (string): Volume dasar dalam unit atomik sebagai string desimal integer tanpa tanda (UInt256String).
  • quote_volume_raw (string): Volume kuotasi dalam unit atomik sebagai string desimal integer tanpa tanda (UInt256String).
  • base_volume (string atau null): Volume token dasar yang dapat dibaca manusia, diskalakan menurut base_decimals sebagai DecimalString; null jika base_decimals tidak diketahui.
  • quote_volume (string atau null): Volume kuotasi yang diskalakan menurut jumlah desimal kuotasi sebagai DecimalString, atau null.

Indikator harga dan VWAP

  • vwap (string atau null): Harga rata-rata tertimbang volume sebagai DecimalString, atau null.
  • first_price (string atau null): Indikator harga pertama sebagai DecimalString, atau null.
  • last_price (string atau null): Indikator harga terakhir sebagai DecimalString, atau null.
  • min_price (string atau null): Indikator harga minimum sebagai DecimalString, atau null.
  • max_price (string atau null): Indikator harga maksimum sebagai DecimalString, atau null.

Bidang pecahan eksak

  • first_price_numerator / first_price_denominator (string): Pembilang dan penyebut integer eksak untuk first_price (UInt256String).
  • last_price_numerator / last_price_denominator (string): Pembilang dan penyebut integer eksak untuk last_price (UInt256String).
  • min_price_numerator / min_price_denominator (string): Pembilang dan penyebut integer eksak untuk min_price (UInt256String).
  • max_price_numerator / max_price_denominator (string): Pembilang dan penyebut integer eksak untuk max_price (UInt256String).
  • refreshed_at (string): Timestamp pembaruan untuk baris ini (timestamp UTC ISO-8601).

Metadata struktur respons (meta)

  • chain: Identitas rantai.
  • chain_slug: Slug rantai kanonis dalam huruf besar.
  • chain_external_id: Identitas rantai dalam format CAIP-2.
  • as_of_block: Blok terbaru rantai yang sudah ditulis sepenuhnya (dilaporkan oleh dataset ini, tidak diperiksa terhadap parameter permintaan).
  • coverage: Klasifikasi cakupan (melaporkan "full" untuk endpoint ini).
  • refreshed_at: Timestamp pembaruan metadata. Dapat bernilai null: null berarti waktu pembaruan data ini tidak diketahui dan data harus dianggap usang; endpoint berbasis blok selalu mengembalikan nilai.

Mengapa harga menggunakan pembilang dan penyebut eksak

Angka JSON standar menggunakan floating-point presisi ganda IEEE-754, yang memiliki keterbatasan presisi:

  1. Pemotongan dan penyimpangan floating-point: Nilai Float64 hanya menyediakan presisi 53 bit, dan pembagian jumlah token menghasilkan penyimpangan pembulatan yang terakumulasi dalam perhitungan.
  2. Keamanan transmisi: Memformat nilai sebagai string desimal (UInt256String) memastikan angka dikirim melalui HTTP tanpa kehilangan presisi di parser JSON.

Dengan menyediakan pembilang dan penyebut integer eksak untuk indikator harga, BlockVectra memungkinkan perhitungan matematika eksak tanpa konversi floating-point.

Menangani pecahan eksak di TypeScript (BigInt)

Di TypeScript, Anda dapat menggunakan BigInt native untuk perbandingan dengan perkalian silang dan konversi fixed-point tanpa konversi floating-point:

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}`;
}

Menangani pecahan eksak di Python

Python menyediakan modul pustaka standar yang khusus dibuat untuk perhitungan rasional dan desimal: fractions.Fraction dan 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}")

Backfill harga harian selama satu tahun

Untuk melakukan backfill data satu tahun (365 hari) dalam batas rentang 90 hari, bagi seluruh rentang tanggal menjadi jendela berurutan yang masing-masing paling lama 90 hari dan kirim permintaan secara bertahap:

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;
}

Perhitungan kapasitas dan penggunaan CU

Setiap endpoint Data API mengukur konsumsi dalam Compute Units (CU). Bobot CU per panggilan untuk data.dex_prices dan perkiraan konsumsi untuk backfill token dihitung di bawah:

Bobot Metodedata.dex_prices: 15 CU / call
  • Pengisian riwayat harga harian 1 tahun untuk 200 token: dengan rentang maksimum 90 hari per permintaan, mencakup 365 hari memerlukan 5 bagian per token, dengan total 1,000 panggilan. Konsumsi total adalah 15,000 CU (sekitar <0.1% dari kuota siklus paket gratis), sekitar <$0.01 dengan harga terdaftar.
  • Pemeliharaan harian (menyegarkan 200 token sekali sehari): 200 panggilan/hari (3,000 CU/hari), dengan total sekitar 6,000 panggilan per siklus 30 hari (90,000 CU, sekitar 0.3% dari kuota gratis), sekitar <$0.01/bulan dengan harga terdaftar.

Saat meningkatkan volume backfill atau memerlukan konkurensi permintaan yang lebih tinggi, lakukan top up on-chain pada halaman Penagihan di konsol untuk meningkatkan akun ke akun berbayar. Untuk tarif dan konversi unit terkini, lihat halaman Harga.

Langkah selanjutnya

Terakhir diperbarui:

Di halaman ini