Documentation Index Fetch the complete documentation index at: https://developer.uphold.com/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers how to compute currency conversions for holdings and transactions and structure the resulting data into a format ready for asynchronous document generation.
Interpret the rates
The rates object uses {asset}-USD keys — each representing the price of 1 unit of the asset in USD. When the denomination is not USD, a USD-{denomination} key is also included:
{
"BTC-USD" : "68106.26" ,
"ETH-USD" : "2094.45" ,
"USD-GBP" : "0.75629"
}
"BTC-USD": "68106.26" means 1 BTC = 68106.26 USD. Multiply by USD-GBP to get the GBP value.
Portfolio rates reflect the end of the requested period. Transaction rates are captured at the time the transaction settled.
Assets already held in the denomination currency (e.g. GBP when denomination is GBP) have no rate entry — their value is their amount directly.
For a detailed explanation of how denomination works in this API, see Denomination in the Core API concepts.
Calculate portfolio values
For each holding, convert the asset’s USD rate by the USD-{denomination} rate:
0.02 BTC × 68106.26 USD/BTC × 0.75629 GBP/USD = 1030.16 GBP
0.5 ETH × 2094.45 USD/ETH × 0.75629 GBP/USD = 792.01 GBP
250.00 GBP = 250.00 GBP (no rate needed)
In code:
function convertToDenomination ( amount , asset , rates , denomination ) {
if ( asset === denomination ) return parseFloat ( amount );
if ( asset === 'USD' ) return parseFloat ( amount ) * parseFloat ( rates [ `USD- ${ denomination } ` ]);
const usdRate = rates [ ` ${ asset } -USD` ];
if ( denomination === 'USD' ) return parseFloat ( amount ) * parseFloat ( usdRate );
return parseFloat ( amount ) * parseFloat ( usdRate ) * parseFloat ( rates [ `USD- ${ denomination } ` ]);
}
function computeHoldingValue ( holding , rates , denomination ) {
return convertToDenomination ( holding . total , holding . asset , rates , denomination ). toFixed ( 2 );
}
Calculate transaction values
Each transaction entry has a denomination field — the amount and currency the transaction was quoted in, used as the authoritative reference value for compliance purposes — and a rates object scoped to that transaction.
To get the GBP value of a transaction, convert the denomination amount using the per-transaction rate:
function computeTransactionValue ( entry , denomination ) {
const { transaction , rates } = entry ;
const { amount , asset } = transaction . denomination ;
return convertToDenomination ( amount , asset , rates , denomination ). toFixed ( 2 );
}
For the GBP→BTC transaction from the previous step, denomination.asset is already GBP, so the GBP value is 100.00 directly — no rate lookup required.
Build the payload
Use the helpers above to transform the raw API response into a flat object ready for the report renderer:
function processStatementData ({ portfolio , transactions , denomination }) {
// Holdings
const holdings = portfolio . holdings . map ( holding => ({
asset: holding . asset ,
amount: holding . total ,
value: computeHoldingValue ( holding , portfolio . rates , denomination )
}));
const totalValue = holdings
. reduce (( sum , h ) => sum + parseFloat ( h . value ), 0 )
. toFixed ( 2 );
// Transactions
const processedTransactions = transactions . map ( entry => {
const { transaction , rates } = entry ;
const txFees = ( transaction . fees ?? [])
. reduce (( sum , fee ) => sum + convertToDenomination ( fee . amount , fee . asset , rates , denomination ), 0 )
. toFixed ( 2 );
return {
date: transaction . completedAt . slice ( 0 , 10 ),
id: transaction . id ,
status: transaction . status ,
origin: { amount: transaction . origin . amount , asset: transaction . origin . asset },
destination: { amount: transaction . destination . amount , asset: transaction . destination . asset },
denominatedAmount: transaction . denomination . amount ,
denominatedAsset: transaction . denomination . asset ,
value: computeTransactionValue ( entry , denomination ),
fees: transaction . fees ?? [],
txFees
};
});
const totalFees = processedTransactions
. reduce (( sum , t ) => sum + parseFloat ( t . txFees ), 0 )
. toFixed ( 2 );
return {
period: portfolio . period ,
denomination ,
holdings ,
totalValue ,
transactions: processedTransactions ,
totalFees
};
}
Applied to the GBP example data from the previous step, this produces:
{
"period" : {
"from" : "2025-03-01T00:00:00.000Z" ,
"to" : "2025-03-31T23:59:59.999Z"
},
"denomination" : "GBP" ,
"holdings" : [
{ "asset" : "BTC" , "amount" : "0.02" , "value" : "1030.16" },
{ "asset" : "ETH" , "amount" : "0.5" , "value" : "792.01" },
{ "asset" : "GBP" , "amount" : "250.00" , "value" : "250.00" }
],
"totalValue" : "2072.17" ,
"transactions" : [
{
"date" : "2025-03-23" ,
"id" : "8daa6dbd-21a0-4305-93c3-bd1d04bc575c" ,
"status" : "completed" ,
"origin" : { "amount" : "100" , "asset" : "GBP" },
"destination" : { "amount" : "0.00186036" , "asset" : "BTC" },
"denominatedAmount" : "100" ,
"denominatedAsset" : "GBP" ,
"value" : "100.00" ,
"fees" : [{ "amount" : "0.5" , "asset" : "GBP" , "type" : "deposit" }],
"txFees" : "0.50"
}
],
"totalFees" : "0.50"
}
Next steps
Generating report Build a PDF from the processed data and run the generation pipeline in a background worker.