トロイオンス、グラム、キログラム:貴金属価格を正しく換算する
トロイオンスと常衡オンスの違い、グラムとキログラムへの正確な換算係数、APIのunitパラメーター、丸めのルール、金属価格が10%近くずれる原因となるバグを解説します。
単位が重要な理由
貴金属の価格は、卸売市場でも、地金商の価格表でも、価格APIでも、ほぼどこでもトロイオンスあたりで提示されます。一方、金属を売買する人々は、住んでいる地域や取引する商品に応じて、グラム、キログラム、トラ、テールといった単位で考えます。そのため、金属価格を人に見せる製品には少なくとも1つの単位換算が含まれており、その換算は、気づかれにくくもっともらしく見える誤りの原因になりがちです。
典型的なミスは、トロイオンスを普通のオンスとして扱ってしまうことです。両者の差は10%近くあり、すべての請求書に影響するほど大きい一方で、デモでは誰も気づかないほど小さな差です。この記事では、厳密な定義、換算係数、APIに換算させる方法、結果の丸め方と表示方法、そしてテストを書いておくべきバグの一覧を紹介します。
トロイオンスと常衡オンス
どちらのオンスもグレーンで定義されており、グレーンにはメートル法での厳密な定義があります。1グレーン = 64.79891ミリグラムです。その他はすべてここから導かれます:
- トロイオンスは480グレーンで、厳密に31.1034768グラムです。金、銀、プラチナ、パラジウムの価格はこの単位で付けられます。
- 常衡オンスは、米国や英国で食品や小包に使われる日常的なオンスで、437.5グレーン、厳密に28.349523125グラムです。
- トロイポンドは12トロイオンス(373.2417216 g)ですが、常衡ポンドは16常衡オンス(453.59237 g)です。つまり、オンスはトロイの方が重いのに、ポンドはトロイの方が軽いのです。
| 単位 | グレーン | グラム(厳密値) | トロイオンス |
|---|---|---|---|
| グレーン | 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で、別の単位を指定しない限り、すべての金属価格はトロイオンスあたりの価格です。
厳密な換算係数
これらの単位はすべて厳密に定義されているため、換算に近似は必要ありません。トロイオンスあたりの価格を別の単位あたりの価格に変えるには、その単位に含まれるトロイオンス数を掛けます:
単位あたりの価格 = トロイオンスあたりの価格 × 1単位のグラム数 ÷ 31.1034768
| 価格の単位 | 1単位のグラム数 | トロイオンス価格に掛ける係数 |
|---|---|---|
| グラム | 1 | 0.0321507466 |
| キログラム | 1,000 | 32.1507466 |
| 常衡オンス | 28.349523125 | 0.9114583333 |
| 常衡ポンド | 453.59237 | 14.5833333 |
| メートルトン | 1,000,000 | 32,150.7466 |
| ショートトン(2,000 lb) | 907,184.74 | 29,166.6667 |
| トラ | 11.6638038 | 0.375 |
| ペニーウェイト | 1.55517384 | 0.05 |
南アジアや湾岸諸国で今も金に広く使われているトラ(tola)は180グレーンと定義されており、厳密に11.6638038 g、つまり3/8トロイオンスです。テール(両)はそれほど明快ではなく、重さが市場によって異なります。香港の金取引では約37.4 g、台湾では37.5 g、中国本土では50 gです。テールに対応する場合は、グラム数を設定値にして、どのテールを指しているかを表示してください。
キロバーは手軽な検算に使えます。1キログラムは約32.15トロイオンスなので、キログラムあたりの価格は常にオンスあたりの価格の約32倍になるはずです。35倍に近い場合は、誰かが常衡オンスの係数を使っています。
手作業で換算する
トロイオンスあたりの価格がすでに手元にあれば、自分で換算するのは掛け算1回です。厳密な定数を1つのモジュールにまとめ、グラムを経由して換算すれば、どの単位の組み合わせでも同じように扱えます:
// 各単位1つあたりのグラム数。すべて厳密値:1グレーン = 64.79891 mg。
const GRAMS_PER_UNIT = {
troy_oz: 31.1034768, // 480グレーン
gram: 1,
kilogram: 1000,
ounce: 28.349523125, // 常衡、437.5グレーン
pound: 453.59237, // 常衡、7,000グレーン
metric_ton: 1_000_000,
short_ton: 907_184.74, // 2,000 lb
tola: 11.6638038, // 180グレーン
pennyweight: 1.55517384, // 24グレーン
};
/** `from`単位あたりの価格を`to`単位あたりの価格に換算します。 */
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.20875Pythonでは、結果が請求書や帳簿に載る場合はDecimalを使ってください。requestsはparse_floatをJSONパーサーに渡すため、価格が2進浮動小数点を経由することはありません:
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"] # 例: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
print(per_kg.quantize(CENT, rounding=ROUND_HALF_UP)) # 128535.15逆数のレートには逆の演算が必要です。rates.XAUは基準通貨1単位あたりのトロイオンス数なので、通貨1単位あたりのグラム数はrates.XAU × 31.1034768で、÷ではありません。この落とし穴も、USDXAUのような通貨コード付きの価格キーを使い、逆数のキーには手を出さないほうがよい理由の1つです。
APIに換算させる:unitパラメーター
必要な単位をMetalAPIに直接指定することもできます。/latest、過去データのエンドポイント、/timeframe、/ohlc、/convert、/caratはunitパラメーターを受け付けます。指定できる値はtroy_oz(デフォルト)、gram、kilogram、ounce、pound、metric_ton、short_tonです。次のリクエストは、金と銀を1グラムあたりのユーロ建てで返します:
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は1ユーロあたりの金のグラム数です。知っておくべきルールがいくつかあります:
g、kg、lb、tonneなどの短縮形も使えます。ozは常衡オンスを意味するため、トロイオンスの略として使わないでください。- 不明な単位を指定すると、エラー
209(invalid_unit)が返されます。 unitが適用されるのは貴金属と産業用金属だけです。エネルギーはバレル、MMBtu、ガロンあたりのまま変わらず、通貨と暗号資産にも影響しないため、1つのリクエストにシンボルを混在させても安全です。- 産業用金属もデフォルトではトロイオンスあたりの価格で提示されるため、すべての金属を直接比較できます。各市場で取引される単位で取得するには、銅は
unit=pound、アルミニウムと亜鉛はunit=metric_ton、鋼はunit=short_tonを指定してください。
/convertもunitを受け付けるため、重量を1ステップで金額に換算できます。次の例は、金250グラムの価値を米ドルで求めます:
curl "https://metalapi.com/api/v1/convert?from=XAU&to=USD&amount=250&unit=gram" \
-H "X-API-Key: $METALAPI_KEY"info.rateは1グラムの米ドル建て価格で、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
}丸めと表示
レートは、最大10桁の有効数字を持つJSONの数値で返されます。この精度は派生値を正確に保つためのものなので、できるだけ最後まで保持してください:
- 先に換算し、丸めは最後に。 グラム価格をセント単位に丸めてから1,000倍してキロバーの価格を求めると、0.5セントの丸め誤差が最大5通貨単位の誤差になります。
- 通貨ごとに丸める。 ほとんどの通貨は小数点以下2桁ですが、日本円は0桁、クウェート・ディナールは3桁です。2桁をハードコードせず、
Intl.NumberFormatやロケールライブラリにデフォルトを選ばせてください。 - 小さな価格には桁を追加する。 例の価格である1トロイオンス53.94 USDでは、銀は1グラムあたり約1.73 USDとなり、小数点以下2桁では日々の値動きのほとんどが見えなくなります。
- 金額には10進数型を使う。 請求や記帳の対象となるものには、Pythonでは
Decimal、JavaではBigDecimal、データベースでは整数の補助通貨単位を使ってください。チャートであれば2進浮動小数点で問題ありません。 - 単位を明記する。 すべての価格の横に「1トロイオンスあたり」「1グラムあたり」「1 kgあたり」と表示してください。「1オンスあたり」だけでは、多くの読者にとって意味があいまいです。
通貨の標準的な桁数を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" 金1グラムあたり
formatPrice(1.7342112699117933, "USD", "en-US", 2); // "$1.7342" 銀1グラムあたり
formatPrice(110.4064149, "EUR", "de-DE"); // "110,41 €"換算表
この表は、MetalAPIのドキュメントで使われている例の価格、つまり金が1トロイオンスあたり3,997.89 USD、銀が1トロイオンスあたり53.94 USDを換算したものです。計算方法を示すためのもので、現在の市場価格ではありません:
| 価格の単位 | 金(USD) | 銀(USD) |
|---|---|---|
| トロイオンス | 3,997.89 | 53.94 |
| グラム | 128.54 | 1.7342 |
| キログラム | 128,535.15 | 1,734.21 |
| トラ(11.6638 g) | 1,499.21 | 20.23 |
| ペニーウェイト(1.5552 g) | 199.89 | 2.697 |
| トロイポンド(12 oz t) | 47,974.68 | 647.28 |
| 常衡オンス | 3,643.91 | 49.16 |
最後の行を見てください。ページで常衡オンスの数値をオンス価格として表示すると、地金価格を9%近く過小に示すことになります。逆に、トロイオンス価格を28.35で割ってグラム価格を求めると約9.7%過大になり、金1グラムあたり128.54ではなく141.02になってしまいます。
よくある換算バグとそのテスト方法
単位に関するバグのほとんどは、いくつかのパターンに分類できます:
- 28.35で割る。 トロイオンス価格に常衡オンスの係数を使っています。31.1034768を使ってください。
- `oz`をトロイオンスとして扱う。 MetalAPIでは、ほとんどの単位ライブラリと同様に、
ozは常衡オンスです。地金の価格にはtroy_ozを指定するか、unitを指定しないでください。 - 逆数のキーを換算する。
rates.XAUは通貨1単位あたりのオンス数であり、価格ではありません。これをグラムに換算するには31.1034768を掛ける必要があり、割り算に慣れた人には誤りに見えます。 - 二重に換算する。
unit=gramをリクエストしたうえでさらに31.1034768で割ると、価格が約31分の1になってしまいます。換算をAPIで行うか自分のコードで行うかを決め、両方では行わないでください。 - 早すぎる丸め。 丸めるのは表示時の1回だけにしてください。
- 単位をまたいだキャッシュ。 キャッシュキーがクエリ文字列を無視していると、オンス価格を期待している箇所でグラムのレスポンスが返されることがあります。キーには
unitとbaseを含めてください。 - エネルギーに重量単位を適用する。
unitは原油やガスには影響せず、バレル価格を31.1で割っても意味がありません。換算はカテゴリで制限してください。カテゴリは/symbols?details=1ですべてのシンボルについて返されます。
いくつかのプロパティテストで、これらのほとんどをリリース前に発見できます。Node.jsの組み込みテストランナーと上記のconvertPrice関数を使っており、node --testで実行できます:
import { test } from "node:test";
import assert from "node:assert/strict";
import { convertPrice } from "./units.mjs";
const PER_OZ = 3997.89; // 例の価格、1トロイオンスあたりUSD
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ドキュメントの単位セクションに記載されており、シンボルページでは各市場で取引される単位を確認できます。
関連記事
金価格API:開発者のための完全ガイド
金価格APIの使い方を解説します。APIキーの取得、curl・JavaScript・Pythonからの呼び出し、レートの正しい読み方から、過去データ、エラー、クォータ、キャッシュの扱いまで網羅しています。
金のカラット別価格を解説:24K・22K・18Kと価格の計算方法
24K・22K・18Kの意味、916や750といった品位表示との関係、カラット別の金価格を1グラムあたりで計算する方法、金価格APIから取得する方法をわかりやすく解説します。
APIで金・銀の過去価格を取得する:25年分の日次データ
/historical、/timeframe、/change、/ohlcで2000年までさかのぼる金・銀の日次価格を取得し、長期間を年単位で分割して保存し、チャートにする方法を解説します。