指南

获取代币的 DEX 日线(开高低收与 VWAP)与精确分数

根据 Data API 规格获取代币的 DEX 每日价格与成交量加权平均价(VWAP),掌握分子分母精确分数表示法、无损精度换算与历史日线分段回填。

什么是 DEX 日线数据集

BlockVectra 的 DEX 数据集涵盖链上去中心化交易所(DEX)的成交记录与日聚合价格。其中,DEX 日线接口(getDexPrices)提供指定代币在指定日期范围内的每日成交量加权平均价(VWAP)、价格指标(字段名 first_price、last_price、min_price、max_price)以及成交量指标。

各链开放该数据集的情况有所不同,提供该数据集的链以支持的链页面为准。

该端点返回指定时间窗口内的数据列表,不进行分页,响应体中永不包含 next_cursor。如果业务需要逐笔具体的成交流水而非按天聚合的日线,请参阅 GET /{chain}/dex/swaps 端点(详见 Data API 参考)。

请求参数与规格限制

DEX 日线请求地址为 GET https://dev-api.blockvectra.network/v1/data/{chain}/dex/prices。所有请求必须在 x-api-key 请求头中传入 API key 进行鉴权。

端点支持以下查询参数:

参数名位置类型是否必填规格说明
chainpath字符串是目标链标识,例如 robinhood_mainnet
tokenquery字符串是20 字节基础代币地址(base token address),0x 前缀可选,大小写均可
quotequery字符串否可选的 20 字节计价代币地址,用于限定为单个基础/计价代币交易对
fromquery日期字符串是UTC 起始日期(包含),格式为 YYYY-MM-DD
toquery日期字符串是UTC 截止日期(包含),格式为 YYYY-MM-DD,且 to - from 必须 <= 90 天

规格约束与错误码

接口在参数不符合规格时返回标准错误结构 {"error":{"code","message"}}:

  • HTTP 400 (bad_request):缺少必需参数(token、from 或 to)、token 或 quote 代币地址格式无效、日期不是合法日历日期,或 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 月份的日线数据:

curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/dex/prices?token=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&from=2026-09-01&to=2026-09-30" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

返回字段详解

data 数组中的每项表示该代币在某一 UTC 日期的交易汇总:

代币与计价信息

  • day(字符串):UTC 日期,格式为 YYYY-MM-DD。
  • token(字符串):20 字节基础代币地址(0x 前缀的小写十六进制)。
  • token_symbol(字符串或 null):基础代币符号。
  • token_name(字符串或 null):基础代币名称。
  • quote_token(字符串):计价资产地址。全零地址(0x0000000000000000000000000000000000000000)代表原生 ETH 作为计价资产。
  • quote_symbol(字符串或 null):计价资产符号,当 quote_token 为全零地址时固定为 "ETH"。
  • quote_name(字符串或 null):计价资产名称,当 quote_token 为全零地址时固定为 "Ether"。
  • base_decimals(整数或 null):基础代币小数位数(0–255)。
  • quote_decimals(整数或 null):计价资产小数位数,当 quote_token 为全零地址时固定为 18。

交易量与成交统计

  • swap_count(整数):该行对应的成交笔数。
  • base_volume_raw(字符串):基础代币原始成交量,以最小单位表示的非负十进制整数字符串(UInt256String)。
  • quote_volume_raw(字符串):计价资产原始成交量,以最小单位表示的非负十进制整数字符串(UInt256String)。
  • base_volume(字符串或 null):经 base_decimals 换算后的基础代币人类可读成交量(DecimalString);若 base_decimals 未知则为 null。
  • quote_volume(字符串或 null):经计价代币小数位换算后的计价成交量(DecimalString),或 null。

价格指标与 VWAP

  • vwap(字符串或 null):成交量加权平均价(以小数定点数字符串表示),或 null。
  • first_price(字符串或 null):首个价格指标(以定点数字符串表示),或 null。
  • last_price(字符串或 null):末个价格指标(以定点数字符串表示),或 null。
  • min_price(字符串或 null):最低价格指标(以定点数字符串表示),或 null。
  • max_price(字符串或 null):最高价格指标(以定点数字符串表示),或 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(字符串):该行的刷新时间(ISO-8601 UTC 时间戳)。

元数据字段 (meta)

  • chain:链标识名称。
  • chain_slug:全大写规范链标识。
  • chain_external_id:CAIP-2 标准外部链 ID。
  • as_of_block:该响应 finality 水位线对应的索引头高度(本数据集仅上报,不参与请求校验)。
  • finalized_block:重组安全水位线区块高度(并非共识最终性)。
  • coverage:覆盖度状态(本端点固定为 "full")。
  • refreshed_at:元数据时间戳。

为什么价格使用分子与分母表示

链上去中心化交易所的价格源自自动做市商(AMM)流动性池的代币储备比值或兑换公式。

直接使用标准 JSON 数字(IEEE-754 双精度浮点数)存在精度局限:

  1. 浮点截断与舍入偏差:双精度浮点数仅有 53 位有效精度,在储备比值除法运算中会产生舍入误差并在后续计算中级联放大。
  2. 传输安全:以十进制整数字符串(UInt256String)表示分子与分母,确保数值在 HTTP 传输与 JSON 解析过程中不丢失精度。

通过返回精确的整数分子与分母,用户可以在不经过浮点数转换的情况下进行计算,在量化策略、套利监测与精确会计核算中避免浮点舍入误差。

在 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 设置自定义高精度(如 50 位有效数字)
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;
}

/**
 * 将任意较长日期区间按最大天数(上限 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;
}

/**
 * 分段回填特定代币的历史日线
 */
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://dev-api.blockvectra.network/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.0015。
  • 日常维护(每天刷新 200 个代币的最新日线):每天调用 200 次(每天消耗 3,000 CU),每个 30 天周期约调用 6,000 次,消耗 9 万 CU(约占免费额度的 0.3%),按标价约每月 $0.009。

当你的回填数据量较大或并发请求较高时,可以在控制台充值以获得更高的速率上限与充裕的计算单元。关于当前的计量单元与定价细则,请参阅定价页。

本页目录