代幣的每日 DEX OHLC 與 VWAP,使用精確分數

用 getDexPrices 端點按代幣與起迄日期查詢每日 VWAP 與開高低收:傳回開高低收價格欄位與分子分母精確分數,單次跨度不得超過 90 天,超出傳回 409 span_exceeded,並提供分段回填寫法。

什麼是 DEX 每日價格資料集

BlockVectra 的 DEX 資料集會索引去中心化交易所交易活動,並計算每日彙總定價指標。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。所有請求都必須在 x-api-key 標頭中提供 API key 進行驗證。

此端點接受下列查詢參數:

參數位置類型必填說明
chainpathstring是鏈識別碼,例如 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 標頭重試。

請求範例

下列範例查詢某個基礎代幣在 2026 年 9 月的每日 DEX 價格:

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 中的每一項代表該代幣對在該 UTC 日期的每日 DEX 彙總指標:

代幣與計價資產

  • 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):計價資產符號(當 quote_token 為全零地址時為 "ETH")。
  • quote_name(string 或 null):計價資產顯示名稱(當 quote_token 為全零地址時為 "Ether")。
  • base_decimals(integer 或 null):基礎代幣小數位數(0–255)。
  • quote_decimals(integer 或 null):計價資產小數位數(當 quote_token 為全零地址時為 18)。

成交量與交易筆數

  • swap_count(integer):此資料列的交換筆數。
  • base_volume_raw(string):以最小單位表示的基礎代幣成交量,為無正負號的十進位整數字串(UInt256String)。
  • quote_volume_raw(string):以最小單位表示的計價代幣成交量,為無正負號的十進位整數字串(UInt256String)。
  • base_volume(string 或 null):依 base_decimals 縮放後的人類可讀基礎代幣成交量,為 DecimalString;當 base_decimals 未知時為 null。
  • 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):此資料列的重新整理時間戳(ISO-8601 UTC 時間戳)。

回應結構中繼資料(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. 不經浮點數轉換的比率比較:檢查收盤價是否高於開盤價
// a / b > c / d  等價於  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. 將分數轉換為指定小數位數的定點十進位字串(不損失浮點精確度)
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. 使用 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"])
)

# 不經浮點數四捨五入誤差的精確價格變動
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. 使用 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}")

回填一年的每日價格

若要在 90 天跨度限制內回填一整年(365 天)的資料,請將完整日期範圍切分為最多 90 天的連續視窗,並發出分段請求:

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

/**
 * 將大型日期範圍切分為最多 maxDays(預設: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;
}

/**
 * 跨多個 90 天分段回填代幣每日價格
 */
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 端點都會以計算單位(CU)計量消耗。data.dex_prices 的每次呼叫 CU 權重,以及代幣回填的預估消耗,計算如下:

方法計費權重data.dex_prices: 15 CU / call
  • 回填 200 個代幣一年日線:單次請求最大跨度為 90 天,一年 365 天需分 5 段請求,單個代幣需 5 次呼叫,總計 1,000 次呼叫。 總計消耗 15,000 CU(約佔單用量週期免費額度的 <0.1%),按標價換算約 <$0.01。
  • 日常維護(每天重新整理 200 個代幣的最新日線):每天呼叫 200 次(每天消耗 3,000 CU),每個 30 天週期約呼叫 6,000 次,消耗 90,000 CU(約佔免費額度的 0.3%),按標價約每月 <$0.01。

當你需要擴充回填量或需要更高的請求並行量時,請在控制台帳單頁面進行鏈上儲值,以升級為付費帳戶。關於目前的費率與單位換算,請參閱定價頁面。

下一步

最後更新:

本頁目錄