获取代币的 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 进行鉴权。
端点支持以下查询参数:
| 参数名 | 位置 | 类型 | 是否必填 | 规格说明 |
|---|---|---|---|---|
chain | path | 字符串 | 是 | 目标链标识,例如 robinhood_mainnet |
token | query | 字符串 | 是 | 20 字节基础代币地址(base token address),0x 前缀可选,大小写均可 |
quote | query | 字符串 | 否 | 可选的 20 字节计价代币地址,用于限定为单个基础/计价代币交易对 |
from | query | 日期字符串 | 是 | UTC 起始日期(包含),格式为 YYYY-MM-DD |
to | query | 日期字符串 | 是 | 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 双精度浮点数)存在精度局限:
- 浮点截断与舍入偏差:双精度浮点数仅有 53 位有效精度,在储备比值除法运算中会产生舍入误差并在后续计算中级联放大。
- 传输安全:以十进制整数字符串(
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。
当你的回填数据量较大或并发请求较高时,可以在控制台充值以获得更高的速率上限与充裕的计算单元。关于当前的计量单元与定价细则,请参阅定价页。