Backtest a gold strategy with historical OHLC
Pull daily OHLC history from the goldprice.dev API and run a simple moving-average crossover backtest in Python to evaluate a gold trading strategy.
Read →How to convert a USD per troy ounce gold price into other currencies and per-gram units, with a working JavaScript example.
The goldprice.dev API returns gold prices in USD per troy ounce. Most developers need to display prices in local currencies, in grams rather than troy ounces, or both. The conversion arithmetic is simple; combining it cleanly with a live exchange rate is where the implementation needs care. If you're new to the weight unit conventions, Troy ounce, gram, kilo: gold weight units explained covers the conversion constants used below.
One troy ounce equals 31.1035 grams. This is a fixed constant; it does not change with market conditions.
To convert a USD/troy oz price to USD/gram:
const GRAMS_PER_TROY_OZ = 31.1035;
function perGram(pricePerTroyOz) {
return pricePerTroyOz / GRAMS_PER_TROY_OZ;
}
If gold is at $2,387.45/oz, the per-gram price is $2,387.45 / 31.1035 = $76.75.
For per-kilogram (common in some markets):
function perKilogram(pricePerTroyOz) {
return (pricePerTroyOz / GRAMS_PER_TROY_OZ) * 1000;
}
Currency conversion requires a separate exchange rate. The goldprice.dev API returns USD prices. You supply the rate. The arithmetic is:
price_in_target = price_in_usd * usd_to_target_rate
Where usd_to_target_rate is how many units of the target currency equal one USD. If 1 USD = 15,800 IDR, then usd_to_target_rate is 15800.
function convertCurrency(usdPrice, exchangeRate) {
return usdPrice * exchangeRate;
}
This example fetches the gold spot price, then converts it to several currencies using a set of exchange rates you provide. In a production app, those exchange rates would come from a live currency API; here they are constants to keep the example self-contained.
const API_KEY = process.env.GOLDPRICE_API_KEY;
const GRAMS_PER_TROY_OZ = 31.1035;
async function getGoldSpot() {
const res = await fetch("https://api.goldprice.dev/v1/spot/XAU-USD-SPOT", {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`API error ${res.status}`);
return res.json();
}
function convertGoldPrice(usdPerOz, exchangeRates) {
const usdPerGram = usdPerOz / GRAMS_PER_TROY_OZ;
return Object.entries(exchangeRates).map(([currency, rate]) => ({
currency,
perTroyOz: parseFloat((usdPerOz * rate).toFixed(2)),
perGram: parseFloat((usdPerGram * rate).toFixed(4)),
}));
}
async function main() {
const spot = await getGoldSpot();
// Replace these with live exchange rates from a currency API
const exchangeRates = {
EUR: 0.921,
GBP: 0.788,
IDR: 15800,
SGD: 1.341,
JPY: 158.4,
};
const conversions = convertGoldPrice(spot.price, exchangeRates);
console.log(`Gold spot: $${spot.price} USD/troy oz`);
console.log(`As of: ${spot.timestamp}`);
console.log("");
for (const c of conversions) {
console.log(`${c.currency}: ${c.perTroyOz}/oz | ${c.perGram}/g`);
}
}
main().catch(console.error);
Sample output with $2,387.45 gold:
Gold spot: $2387.45 USD/troy oz
As of: 2026-06-19T08:32:11Z
EUR: 2198.82/oz | 70.69/g
GBP: 1881.31/oz | 60.48/g
IDR: 37721710.00/oz | 1212630.6484/g
SGD: 3202.57/oz | 102.97/g
JPY: 378171.48/oz | 12161.8135/g
Different locales format numbers differently. JavaScript's Intl.NumberFormat handles this cleanly:
function formatPrice(amount, currency) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
maximumFractionDigits: currency === "JPY" || currency === "IDR" ? 0 : 2,
}).format(amount);
}
console.log(formatPrice(2198.82, "EUR")); // €2,198.82
console.log(formatPrice(37721710, "IDR")); // Rp37,721,710
Adjust the locale string to match your target audience. id-ID for Indonesian Rupiah, ja-JP for Japanese Yen, and so on. The currency code and locale are independent, so you can display IDR in English format or Japanese format depending on what your users expect.
Exchange rates change throughout the trading day. For a price display that does not need to be precise to fractions of a percent, updating the exchange rates once per hour is acceptable. For applications where currency accuracy matters, such as pricing for transactions, update the exchange rates on the same cadence as the gold price.
A simple in-memory cache with a configurable TTL keeps call volume manageable:
const rateCache = { rates: null, fetchedAt: 0 };
const RATE_TTL_MS = 60 * 60 * 1000; // 1 hour
async function getExchangeRates() {
if (rateCache.rates && Date.now() - rateCache.fetchedAt < RATE_TTL_MS) {
return rateCache.rates;
}
// fetch from your currency rate provider here
const rates = await fetchRatesFromProvider();
rateCache.rates = rates;
rateCache.fetchedAt = Date.now();
return rates;
}
| Conversion | Formula |
|---|---|
| USD/oz to USD/g | price / 31.1035 |
| USD/oz to USD/kg | (price / 31.1035) * 1000 |
| USD/oz to TARGET/oz | price * rate |
| USD/oz to TARGET/g | (price / 31.1035) * rate |
All four conversions come down to the same two constants: 31.1035 (troy oz to grams) and the current exchange rate. Separate your data fetching from your conversion logic, and the code stays easy to test and extend. Start with the quickstart guide if you haven't fetched a price yet.
// related guides
Pull daily OHLC history from the goldprice.dev API and run a simple moving-average crossover backtest in Python to evaluate a gold trading strategy.
Read →Pull gold and silver spot prices in a single async call, divide them, and display the ratio with a working JavaScript example.
Read →Read per-source prices from the API response, compute the spread in basis points, and flag when sources disagree beyond your tolerance threshold.
Read →// goldprice.dev
Live gold prices, historical OHLC, and multi-source aggregation — available via REST and SSE.