Case Study/生产环境案例研究

goldprice.dev 如何以本地货币计价黄金

goldprice.dev 如何将美元计价的金属价格与 exchangerate.dev 的外汇汇率相结合,在不掩盖数据新鲜度边界的前提下提供本地货币黄金价格。

MMexchangerate.dev·Jul 16, 2026·7 分钟阅读

goldprice.dev 和 exchangerate.dev 是由同一家公司运营的兄弟产品。在生产环境中,goldprice.dev 负责金属价格契约,而 exchangerate.dev 为支持的浮动货币提供外汇端数据。这一边界很重要:一个本地黄金价格包含两个市场观测值,其中一端数据新鲜并不能让另一端也变得新鲜。

Key points
goldprice.dev 负责金属价格和公开的换算响应;exchangerate.dev 提供外汇端数据。
公开的 goldprice.dev /v1/convert 端点提供了单次调用的生产路径。
当开发者需要可审计的数据溯源时,可以分别调用这两个 API。
应分别存储黄金的 computed_at 以及外汇的 data_updated_attimestampsourcemarket_session
换算后的价格是指示性市场价值,而非可执行的经销商报价或结算报价。

生产环境边界

goldprice.dev 从美元计价的金属观测值出发。exchangerate.dev 为支持的浮动货币提供相应的美元兑本地货币外汇观测值。随后,goldprice.dev 负责特定于金属的换算契约,包括单位、支持的代码、校验逻辑和最终响应。

这是一次已披露的兄弟产品集成,而非独立客户的背书。它作为案例研究的价值在于运营层面:外汇 API 提供了真实的生产数据端,而调用方产品则保留了对自身领域的责任。

换算公式

黄金通常以每金衡盎司的美元价格报价。本地展示通常需要每克的货币金额。请先换算重量单位,再套用美元汇率:

formula · USD/oz to local currency/gramcopy
const GRAMS_PER_TROY_OZ = 31.1035;

function localGoldPerGram(usdPerTroyOz, usdToLocalRate) {
  return (usdPerTroyOz / GRAMS_PER_TROY_OZ) * usdToLocalRate;
}

重量常数是固定的。但两个市场输入并非如此:黄金报价和外汇汇率各自都有自己的观测时间和新鲜度规则。

单次调用的生产路径

对调用方而言,生产环境边界就是 goldprice.dev `/v1/convert`。它在一个了解金属属性的端点背后整合了金属和外汇两端数据,且无需 API 密钥。以下示例请求一克黄金对应的印尼盾价值:

javascript · one-request conversioncopy
const params = new URLSearchParams({
  from: "XAU",
  to: "IDR",
  amount: "1",
  unit: "gram",
});

const response = await fetch(
  `https://api.goldprice.dev/v1/convert?${params}`,
);

if (!response.ok) {
  throw new Error(`goldprice.dev returned ${response.status}`);
}

const conversion = await response.json();
console.log(conversion.result, conversion.to, conversion.timestamp);

这条路径让调用方契约保持精简:一次请求、一个金额、一个金属单位、一个目标货币。它唯一的 timestamp 并不能独立证明两个市场端各自的新鲜度,因此需要审计外汇新鲜度的调用方应使用下文的双端路径。

可审计的双端集成

当你需要保留每个数据源各自的时间戳、独立缓存输入,或者说明哪一端数据已过期时,请分别调用这两个 API。goldprice.dev 现货价格提供每金衡盎司的美元黄金价格;exchangerate.dev 提供美元兑本地货币的汇率。

javascript · explicit gold and FX legscopy
const GRAMS_PER_TROY_OZ = 31.1035;

async function getGoldInCurrencies(currencies) {
  const symbols = currencies.join(",");
  const [spotResponse, fxResponse] = await Promise.all([
    fetch("https://api.goldprice.dev/v1/spot/XAU-USD-SPOT"),
    fetch(`https://api.exchangerate.dev/v1/latest/USD?symbols=${symbols}`),
  ]);

  if (!spotResponse.ok || !fxResponse.ok) {
    throw new Error("A market-data request failed");
  }

  const spot = await spotResponse.json();
  const fx = await fxResponse.json();
  const usdPerGram = Number(spot.price) / GRAMS_PER_TROY_OZ;

  return {
    goldComputedAt: spot.computed_at,
    fxDataUpdatedAt: fx.data_updated_at,
    fxResponseTimestamp: fx.timestamp,
    fxSource: fx.source,
    fxMarketSession: fx.market_session,
    prices: Object.fromEntries(
      currencies.map((currency) => [
        currency,
        usdPerGram * Number(fx.rates[currency]),
      ]),
    ),
  };
}

const result = await getGoldInCurrencies(["EUR", "IDR", "JPY"]);
响应时间戳不等于市场时间戳
请保留 spot.computed_at 作为黄金观测时间,以及 fx.data_updated_at 作为参与计算的最旧外汇观测时间。外汇的 timestamp 表示响应的生成时间;source 和 market_session 则补充了数据类别和交易时段的上下文。不要用 Date.now() 替代这些字段。

为什么两项服务仍保持独立

这次集成并没有让 exchangerate.dev 变成完整的黄金定价引擎,它仍然只是外汇层。保持这一边界清晰,可以避免货币新鲜度、金属新鲜度和特定产品的换算规则混杂成一个含糊不清的数值。

  • goldprice.dev 可以暴露一个小巧的、特定于金属的接口,而无需调用方自行拼装常见路径。
  • exchangerate.dev 保留了一个通用的外汇契约,在金属领域之外同样有用。
  • 对审计敏感的调用方可以分别获取并存储这两个观测值。
  • 每项服务都可以用自身市场数据的语汇来报告故障和新鲜度。

显式处理过期或缺失的数据端

如果任一输入缺失、为零、非数值,或超出你产品所接受的新鲜度窗口,组合价格就不应继续生效。只有在接口用原始观测时间对其加以标注的情况下,才可以保留最后一个有效值。

  • 在进行运算之前,先检查两个 HTTP 响应。
  • 拒绝非有限或非正的价格与汇率。
  • 为黄金和外汇分别设置新鲜度阈值。
  • market_session: weekend 当作上下文信息,而不是用来证明延续使用的汇率是最新的。
  • 在换算错误日志中记录货币对以及两个底层观测时间。

在展示时四舍五入,而非在计算过程中

在乘法运算过程中保留完整精度,最终的小数位数交由展示所用的地区设置决定。Intl.NumberFormat 会处理货币符号和分组,且不会改变底层数值:

javascript · locale-aware displaycopy
function formatPrice(amount, currency, locale) {
  return new Intl.NumberFormat(locale, {
    style: "currency",
    currency,
  }).format(amount);
}

console.log(formatPrice(result.prices.IDR, "IDR", "id-ID"));
console.log(formatPrice(result.prices.JPY, "JPY", "ja-JP"));

将结果作为指示性数值使用

计算得出的市场价值并不是金币或金条的零售价格。经销商溢价、税费、加工费、支付手续费以及可执行的外汇点差都不在这个公式的考虑范围内。除非你的应用另外加入真实的经销商报价及其商业条款,否则应将输出标注为指示性数值。

这个案例研究证明了什么

goldprice.dev 是第一方证据,证明 exchangerate.dev 能够为生产环境定价工作流提供外汇端数据,而不是独立的社会认同证明。真正有用的证明范围更窄、也可复现:开发者可以调用同样的公开产品,保留两个观测时间,并自行验证这套运算。

MM
exchangerate.dev
面向开发者的生产环境集成说明。

Keep reading

Reference解读 source 与 market_session 字段Read Tutorial在 JavaScript 与 Node.js 中进行货币换算Read Guide指示性汇率与可执行汇率的区别Read