Тройские унции, граммы и килограммы: как правильно пересчитывать цены на драгоценные металлы
Тройская и торговая унция, точные коэффициенты для граммов и килограммов, параметр unit в API, правила округления и ошибки, искажающие цены почти на 10%.
Почему единица измерения важна
Драгоценные металлы почти везде котируются за тройскую унцию: на оптовых рынках, в прайс-листах дилеров слитков и в API цен. А люди, которые покупают и продают металл, мыслят граммами, килограммами, тола или таэлями — в зависимости от того, где они живут и чем торгуют. Поэтому любой продукт, который показывает людям цену металла, содержит хотя бы один пересчёт единиц, и этот пересчёт — частый источник незаметных, правдоподобно выглядящих ошибок.
Классическая ошибка — считать тройскую унцию обычной унцией. Они различаются почти на 10%: этого достаточно, чтобы разница сказалась в каждом счёте, но слишком мало, чтобы кто-то заметил её на демонстрации. В этой статье — точные определения, коэффициенты пересчёта, способ поручить пересчёт API, правила округления и отображения результатов, а также список ошибок, на которые стоит написать тесты.
Тройская унция и торговая унция (avoirdupois)
Обе унции определены через граны, а у грана есть точное метрическое определение: 1 гран = 64,79891 миллиграмма. Всё остальное следует из него:
- Тройская унция — это 480 гран, ровно 31,1034768 грамма. Именно в ней оцениваются золото, серебро, платина и палладий.
- Торговая унция (avoirdupois) — обычная унция для продуктов и посылок в США и Великобритании — это 437,5 грана, ровно 28,349523125 грамма.
- Тройский фунт состоит из 12 тройских унций (373,2417216 г), а торговый фунт — из 16 торговых унций (453,59237 г). Так что тройская унция тяжелее торговой, а тройский фунт, наоборот, легче торгового.
| Единица | Граны | Граммы (точно) | Тройские унции |
|---|---|---|---|
| Гран | 1 | 0,06479891 | 0,0020833… |
| Пеннивейт (dwt) | 24 | 1,55517384 | 0,05 |
| Торговая унция | 437,5 | 28,349523125 | 0,9114583… |
| Тройская унция | 480 | 31,1034768 | 1 |
| Тройский фунт | 5 760 | 373,2417216 | 12 |
| Торговый фунт | 7 000 | 453,59237 | 14,5833… |
Тройская унция примерно на 9,7% тяжелее торговой (480 ÷ 437,5 ≈ 1,0971). Ровно такого размера ошибку вы получите, если их перепутаете.
Если источник не указывает иное, унция в цене драгоценного металла — тройская; её часто обозначают oz t или ozt. В MetalAPI единица по умолчанию — troy_oz, и цена любого металла указывается за тройскую унцию, если вы не запросили другую единицу.
Точные коэффициенты пересчёта
Поскольку все эти единицы определены точно, для пересчёта не нужны приближения. Чтобы перевести цену за тройскую унцию в цену за другую единицу, умножьте её на количество тройских унций в этой единице:
цена за единицу = цена за тройскую унцию × граммов в единице ÷ 31,1034768
| Цена за | Граммов в единице | Умножьте цену за тройскую унцию на |
|---|---|---|
| Грамм | 1 | 0,0321507466 |
| Килограмм | 1 000 | 32,1507466 |
| Торговую унцию | 28,349523125 | 0,9114583333 |
| Торговый фунт | 453,59237 | 14,5833333 |
| Метрическую тонну | 1 000 000 | 32 150,7466 |
| Короткую тонну (2 000 фунтов) | 907 184,74 | 29 166,6667 |
| Тола | 11,6638038 | 0,375 |
| Пеннивейт | 1,55517384 | 0,05 |
Тола, которая по-прежнему широко используется для золота в Южной Азии и странах Персидского залива, определяется как 180 гран: ровно 11,6638038 г, или три восьмых тройской унции. С таэлем всё не так аккуратно, потому что его масса зависит от рынка: около 37,4 г в торговле золотом в Гонконге, 37,5 г на Тайване и 50 г в континентальном Китае. Если вы поддерживаете таэли, сделайте массу в граммах параметром конфигурации и указывайте, какой именно таэль имеется в виду.
Килограммовые слитки — удобная проверка на здравый смысл. В килограмме около 32,15 тройской унции, поэтому цена за килограмм всегда должна быть примерно в 32 раза выше цены за унцию. Если разница ближе к 35 разам, кто-то использовал коэффициент торговой унции.
Пересчёт вручную
Если у вас уже есть цена за тройскую унцию, пересчитать её самостоятельно можно одним умножением. Храните точные константы в одном модуле и пересчитывайте через граммы — тогда любая пара единиц работает одинаково:
// Grams in one of each unit. All exact: 1 grain = 64.79891 mg.
const GRAMS_PER_UNIT = {
troy_oz: 31.1034768, // 480 grains
gram: 1,
kilogram: 1000,
ounce: 28.349523125, // avoirdupois, 437.5 grains
pound: 453.59237, // avoirdupois, 7,000 grains
metric_ton: 1_000_000,
short_ton: 907_184.74, // 2,000 lb
tola: 11.6638038, // 180 grains
pennyweight: 1.55517384, // 24 grains
};
/** Convert a price per `from` unit into a price per `to` unit. */
export function convertPrice(price, from, to) {
const a = GRAMS_PER_UNIT[from];
const b = GRAMS_PER_UNIT[to];
if (a === undefined || b === undefined) throw new Error(`Unknown unit: ${a === undefined ? from : to}`);
return (price / a) * b;
}
console.log(convertPrice(3997.89, "troy_oz", "gram")); // 128.535148…
console.log(convertPrice(3997.89, "troy_oz", "kilogram")); // 128535.148…
console.log(convertPrice(3997.89, "troy_oz", "tola")); // 1499.20875В Python используйте Decimal, если результат попадает в счёт или в учётные записи. requests передаёт parse_float в JSON-парсер, поэтому цены никогда не проходят через двоичные числа с плавающей точкой:
import os
from decimal import Decimal, ROUND_HALF_UP
import requests
GRAMS_PER_TROY_OZ = Decimal("31.1034768")
CENT = Decimal("0.01")
r = requests.get(
"https://metalapi.com/api/v1/latest",
params={"currencies": "XAU"},
headers={"X-API-Key": os.environ["METALAPI_KEY"]},
timeout=10,
)
data = r.json(parse_float=Decimal)
if not data["success"]:
raise RuntimeError(data["error"])
per_oz = data["rates"]["USDXAU"] # e.g. Decimal("3997.89")
per_gram = per_oz / GRAMS_PER_TROY_OZ
per_kg = per_gram * 1000
print(per_gram.quantize(CENT, rounding=ROUND_HALF_UP)) # 128.54 at the example price
print(per_kg.quantize(CENT, rounding=ROUND_HALF_UP)) # 128535.15Для обратных курсов нужна противоположная операция. rates.XAU — это количество тройских унций на одну единицу базовой валюты, поэтому количество граммов на единицу валюты равно rates.XAU × 31,1034768, а не ÷. Эта ловушка — ещё одна причина работать с ключами цен с префиксом, такими как USDXAU, и не трогать обратные ключи.
Пересчёт на стороне API: параметр unit
Можно также запросить у MetalAPI нужную единицу. /latest, исторические эндпоинты, /timeframe, /ohlc, /convert и /carat принимают параметр unit: troy_oz (по умолчанию), gram, kilogram, ounce, pound, metric_ton или short_ton. Этот запрос возвращает цены золота и серебра в евро за грамм:
curl "https://metalapi.com/api/v1/latest?base=EUR¤cies=XAU,XAG&unit=gram" \
-H "X-API-Key: $METALAPI_KEY"Если единица отличается от значения по умолчанию, ответ повторяет её в поле unit верхнего уровня. Значения ниже — пример из документации, а не актуальные цены:
{
"success": true,
"timestamp": 1792765800,
"date": "2026-10-23",
"base": "EUR",
"unit": "gram",
"rates": {
"XAU": 0.009057444725,
"EURXAU": 110.4064149,
"XAG": 0.6713138244,
"EURXAG": 1.489616277
}
}API использует те же точные коэффициенты, что и таблицы выше, а обратный ключ тоже следует за единицей: при unit=gram значение rates.XAU — это граммы золота за один евро. Несколько правил, которые стоит знать:
- Принимаются краткие формы, например
g,kg,lbиtonne.ozозначает торговую унцию, поэтому никогда не используйте его как сокращение для тройской. - Неизвестная единица возвращает ошибку
209(invalid_unit). unitприменяется только к драгоценным и промышленным металлам. Энергоносители остаются в баррелях, MMBtu или галлонах, а валюты и криптовалюты не затрагиваются, так что смешивать символы в одном запросе безопасно.- Промышленные металлы по умолчанию тоже котируются за тройскую унцию, поэтому все металлы можно напрямую сравнивать. Передайте
unit=poundдля меди,unit=metric_tonдля алюминия и цинка иunit=short_tonдля стали, чтобы получить единицы, в которых торгуют на этих рынках.
/convert тоже принимает unit, что позволяет одним шагом превратить массу в деньги. Этот запрос оценивает 250 граммов золота в долларах США:
curl "https://metalapi.com/api/v1/convert?from=XAU&to=USD&amount=250&unit=gram" \
-H "X-API-Key: $METALAPI_KEY"info.rate — цена одного грамма в долларах США, а result — стоимость всего количества. При примерной цене золота ответ выглядит так:
{
"success": true,
"query": { "from": "XAU", "to": "USD", "amount": 250, "unit": "gram" },
"info": { "timestamp": 1792834892, "rate": 128.5351482, "quote": 128.5351482 },
"date": "2026-10-24",
"result": 32133.78705
}Округление и отображение
Курсы приходят в виде чисел JSON с точностью до 10 значащих цифр. Эта точность нужна, чтобы производные значения оставались точными, поэтому сохраняйте её до последнего момента:
- Сначала пересчитывайте, потом округляйте. Если округлить цену грамма до центов, а затем умножить её на 1 000 для килограммового слитка, ошибка округления в полцента превратится в ошибку до пяти денежных единиц.
- Округляйте с учётом валюты. В большинстве валют два знака после запятой, в японской иене — ни одного, а в кувейтском динаре — три. Позвольте
Intl.NumberFormatили вашей библиотеке локализации выбрать значения по умолчанию, вместо того чтобы жёстко прописывать два знака. - Оставляйте больше знаков для малых цен. При примерной цене 53,94 USD за тройскую унцию серебро стоит около 1,73 USD за грамм, и два знака после запятой скрывают большую часть его ежедневных колебаний.
- Используйте десятичные типы для денег.
Decimalв Python,BigDecimalв Java или целые числа в минимальных единицах валюты в базе данных — для всего, что выставляется в счёте или проводится в учёте. Для графиков двоичные числа с плавающей точкой вполне подходят. - Подписывайте единицу. Ставьте «за тройскую унцию», «за грамм» или «за кг» рядом с каждой ценой. Просто «за унцию» многие читатели поймут неоднозначно.
Небольшая вспомогательная функция, которая берёт стандартное число знаков валюты из Intl и при необходимости добавляет ещё несколько:
function formatPrice(value, currency, locale = "en-US", extraDigits = 0) {
const { maximumFractionDigits } = new Intl.NumberFormat(locale, { style: "currency", currency }).resolvedOptions();
const digits = maximumFractionDigits + extraDigits;
return new Intl.NumberFormat(locale, {
style: "currency",
currency,
minimumFractionDigits: digits,
maximumFractionDigits: digits,
}).format(value);
}
formatPrice(128.53514819925212, "USD"); // "$128.54" gold per gram
formatPrice(1.7342112699117933, "USD", "en-US", 2); // "$1.7342" silver per gram
formatPrice(110.4064149, "EUR", "de-DE"); // "110,41 €"Таблица пересчёта
В таблице пересчитаны примерные цены из документации MetalAPI: 3 997,89 USD за тройскую унцию для золота и 53,94 USD за тройскую унцию для серебра. Они иллюстрируют арифметику и не являются текущими рыночными ценами:
| Цена за | Золото (USD) | Серебро (USD) |
|---|---|---|
| Тройскую унцию | 3 997,89 | 53,94 |
| Грамм | 128,54 | 1,7342 |
| Килограмм | 128 535,15 | 1 734,21 |
| Тола (11,6638 г) | 1 499,21 | 20,23 |
| Пеннивейт (1,5552 г) | 199,89 | 2,697 |
| Тройский фунт (12 oz t) | 47 974,68 | 647,28 |
| Торговую унцию | 3 643,91 | 49,16 |
Посмотрите на последнюю строку. Если на странице значение для торговой унции подписано как цена за унцию, цена слитков занижается почти на 9%. Обратная ошибка — делить цену за тройскую унцию на 28,35, чтобы получить цену грамма, — завышает её примерно на 9,7%: 141,02 вместо 128,54 за грамм золота.
Типичные ошибки пересчёта и как их тестировать
Большинство ошибок с единицами укладываются в несколько шаблонов:
- Деление на 28,35. Использование коэффициента торговой унции для цены за тройскую унцию. Используйте 31,1034768.
- Восприятие `oz` как тройской унции. В MetalAPI, как и в большинстве библиотек единиц,
oz— это торговая унция. Для цен на слитки нуженtroy_ozили отсутствиеunitвообще. - Пересчёт обратного ключа.
rates.XAU— это унции на единицу валюты, а не цена. Чтобы перевести его в граммы, нужно умножить на 31,1034768, что выглядит неправильно для любого, кто привык делить. - Двойной пересчёт. Если запросить
unit=gram, а затем снова разделить на 31,1034768, получится цена, примерно в 31 раз меньше нужной. Решите, где происходит пересчёт — в API или в вашем коде, но никогда не в обоих местах. - Слишком раннее округление. Округляйте один раз — при отображении.
- Кэш без учёта единиц. Если ключ кэша не учитывает строку запроса, ответ в граммах может быть отдан там, где ожидалась цена за унцию. Включайте
unitиbaseв ключ. - Применение единиц массы к энергоносителям.
unitне влияет на нефть и газ, а делить цену барреля на 31,1 бессмысленно. Ограничивайте пересчёт по категориям —/symbols?details=1возвращает категорию каждого символа.
Несколько тестов свойств выявят большинство из этих ошибок до выпуска. Они используют встроенный в Node тестовый раннер и функцию convertPrice из примера выше; запускайте их командой node --test:
import { test } from "node:test";
import assert from "node:assert/strict";
import { convertPrice } from "./units.mjs";
const PER_OZ = 3997.89; // example price, USD per troy ounce
test("a kilogram price is 1,000 gram prices", () => {
const g = convertPrice(PER_OZ, "troy_oz", "gram");
const kg = convertPrice(PER_OZ, "troy_oz", "kilogram");
assert.ok(Math.abs(kg - g * 1000) < 1e-6);
});
test("converting there and back returns the original price", () => {
const g = convertPrice(PER_OZ, "troy_oz", "gram");
assert.ok(Math.abs(convertPrice(g, "gram", "troy_oz") - PER_OZ) < 1e-9);
});
test("an avoirdupois ounce costs less than a troy ounce", () => {
assert.ok(convertPrice(PER_OZ, "troy_oz", "ounce") < PER_OZ);
});
test("a kilogram holds about 32.15 troy ounces", () => {
const kg = convertPrice(PER_OZ, "troy_oz", "kilogram");
assert.ok(Math.abs(kg / PER_OZ - 32.1507466) < 1e-6);
});Если вы оцениваете ювелирные изделия, следующий шаг — проба: статья «Цены на золото по каратам» строит цены 22K и 18K на основе этих пересчётов. Руководство по форматам данных и раздел о единицах в документации API описывают все значения единиц, а на странице символов указано, в какой единице торгуется каждый рынок.
Попробуйте на своих данных
Создайте бесплатный аккаунт и отправьте первый запрос меньше чем за две минуты. 100 запросов в месяц, без банковской карты.
Читайте также
API цен на золото: полное руководство для разработчиков
Как работать с API цен на золото: получить ключ, вызвать API из curl, JavaScript и Python, правильно читать курсы, работать с историей, ошибками, квотами и кэшем.
Цены на золото по каратам: 24K, 22K, 18K и как их рассчитать
Что означают 24K, 22K и 18K, как с ними связаны пробы 916 и 750, как рассчитать цену грамма золота по каратам и как получить её через API цен на золото.
Исторические цены на золото и серебро через API: 25 лет дневных данных
Дневные исторические цены на золото и серебро с 2000 года через /historical, /timeframe, /change и /ohlc: загрузка по годам, хранение данных и графики.