Background Gradient

Developer Hub

Advanced Functions
and Integrations

Daily Volume-Weighted Average Price Reference:

Calculation Time:
Executed daily at 00:05:00 UTC, using trade data from the previous UTC day (00:00:00 to 23:59:59).

Data Sources:
Trades are aggregated from: Biconomy, BingX, BitGet, BitMart, Coinex, MEXC, and XeggeX.

Calculation Logic:

  • Hourly Partitioning: The day is split into 24 one-hour intervals.
  • Volume-Based Quartile Filtering: Within each hour, trades are sorted by price and total volume is calculated. The lowest 25% and highest 25% of cumulative volume are excluded to remove outliers. If a trade crosses a quartile boundary, its volume is split to maintain exactly 50% total volume.
  • Hourly VWAP Calculation: For the middle 50% volume:
    • Each trade’s price is multiplied by its volume.
    • The results are summed and divided by the filtered volume to get the volume-weighted average price.
  • Daily Price Derivation: The arithmetic mean of all 24 hourly VWAPs is taken. If any hour has zero volume, the daily price is not calculated to ensure consistency.

Design Benefits:

  • TWAP-style time weighting
  • Volume sensitivity
  • Outlier resistance via quartile trimming

Click on the API Endpoints or Example API Requests buttons below for information on how to access this data.

Hourly Volume-Weighted Average Price Reference:

Calculation Time:
Price calculated hourly at {hour}:05:00 UTC, using trade data from the previous hour.

Data Sources:
Trades are aggregated from: BingX, BitGet, BitMart, Coinex, and MEXC.

Calculation Logic:

  • Quarterly Partitioning: Each hour is split into four 15-minute intervals.
  • Volume-Based Quartile Filtering: Within each 15-minute interval, trades are sorted by price and total volume is calculated. The lowest 25% and highest 25% of cumulative volume are excluded to remove outliers. If a trade crosses a quartile boundary, its volume is split to preserve exactly 50% of total volume.
  • Quarterly VWAP Calculation: For the filtered 50% of volume:
    • Each trade’s price is multiplied by its volume.
    • The result is summed and divided by the mid-volume total to compute the volume-weighted price for the interval.
  • Hourly Price Derivation: The arithmetic mean of the four 15-minute VWAPs is taken. If any quarter lacks volume, the hourly price is not calculated.

Design Benefits:

  • TWAP-style time weighting
  • Volume sensitivity
  • Outlier resistance via quartile trimming

Click on the API Endpoints or Example API Requests buttons below for information on how to access this data.

CURRENT DAILY AVERAGE:
wallywallet.org/_api/v0/now/dailyavg/usdt/nexa

HISTORIC LOOKUP DAILY AVERAGE:
wallywallet.org/_api/v0/dailyavg/usdt/nexa?time=< epoch seconds >

CURRENT HOURLY AVERAGE:
wallywallet.org/_api/v0/now/hourlyavg/usdt/nexa

HISTORIC LOOKUP HOURLY AVERAGE:
wallywallet.org/_api/v0/hourlyavg/usdt/nexa?time=< epoch seconds >

Current Averages require no query parameters, and reflect price for the previous period - day or hour - as of the time the request is made. If a price was not calculated for that period, the previous period's price is returned. Prices are calculated 5 minutes after the day or hour. All times are given in UTC. Prices are in units per Nexa Satoshi.

Historic Averages require one query parameter: time (in epoch seconds). The time parameter must be less than or equal to current time, to be considered valid. These endpoints return a price for the previous period - day or hour (if one was calculated) - prior to the value of epoch seconds given. Prices are calculated 5 minutes after the day or hour. All times are given in UTC. Prices are in units per Nexa Satoshi.

All APIs return a JSON object of:
{
  "type": String,
  "msg": {
    "data": String (32 byte hex),
    "signature": String (64 byte hex)
  },
  "epochSeconds": Long,
  "price": String,
  "pairPriceUnit": String
}

Example response:

{
  "type": "Hourly Average",
  "msg": {
    "data": "4e455841000000005553445400000004f16ab6600000000b0b59ff905000000",
    "signature": "2ae6eaddb3cd31842ceeb42079c7a25a86ec79f6e9c24c1b303007ee1dbeef24d313f90ba16a47b5e26106a904f4cfe86cea007c8373336382f490004999ab893"
  },
  "epochSeconds": 1722488399,
  "price": "0.000002566283000",
  "pairPriceUnit": "USDT/NEXA"
}

EXAMPLE API REQUEST:

Current Daily Average:

http://wallywallet.org/_api/v0/now/dailyavg/usdt/nexa

Current Hourly Average:

http://wallywallet.org/_api/v0/now/hourlyavg/usdt/nexa

Historic Lookup Daily Average:

http://wallywallet.org/_api/v0/dailyavg/usdt/nexa?time=1722489634

Historic Lookup Hourly Average:

http://wallywallet.org/_api/v0/hourlyavg/usdt/nexa?time=1722489634

Example msg.data parsing with kotlin code.

Returns parsed data object with four fields:

tickerA, tickerB, epochSeconds, and price

@Serializable
data class OracleObject(
    val type: String,
    val msg: OracleMsg,
    val epochSeconds: Long,
    @Serializable(with = BigDecimalSerializer::class)val price: BigDecimal)
    val pairPriceUnit: String
)

data class ParsedOracleData(
    val tickerA: String,
    val tickerB: String,
    val epochSeconds: Long,
    val price: Long
)

fun parseOracleData(data: ByteArray): ParsedOracleData {
    require(data.size == 24) { "Data must be exactly 24 bytes" }

    val tickerA = String(data.sliceArray(0..3)).trimEnd { it.toInt() == 0 }
    val tickerB = String(data.sliceArray(4..7)).trimEnd { it.toInt() == 0 }

    val longBuffer1 = ByteBuffer.wrap(data.sliceArray(8..15))
                            .order(ByteOrder.LITTLE_ENDIAN)
    val epochSeconds = longBuffer1.long

    val longBuffer2 = ByteBuffer.wrap(data.sliceArray(16..23))
                            .order(ByteOrder.LITTLE_ENDIAN)
    val price = longBuffer2.long

    return ParsedOracleData(tickerA, tickerB, epochSeconds, price)
}


val response = client.get("https://wallywallet.org/_api/v0/hourlyavg/usdt/nexa?
                        time=1724382065").bodyAsText()
val message = Json.decodeFromString<OracleObject>(response).msg
val data = message.data
val signature = message.signature

val byteArray = data.fromHex()
val parsedOracleData = parseOracleData(byteArray)

Example msg.signature validation with kotlin code.

code goes here

PARSING OF PRICE ORACLE API's MSG.DATA:

Data is stored in a 24-byte packed structure, which consists of the following:

  • 4-byte zero-padded byte array containing a string representing tickerA
  • 4-byte zero-padded byte array containing a string representing tickerB
  • 8-byte (little endian) integer representing epoch seconds at the end of the calculated interval
  • 8-byte (little endian) integer representing the calculated price x 1e16

Since decimal values are prone to rounding errors, the 1e16 multiplier is used to transfer a more stable Long value. An 8-byte Kotlin Long can support values up to about 9.22 x 1e18, therefore providing support of prices up to about 922 units of a given currency, per Nexa Satoshi.

msg.data is this 24-byte packed structure, converted to a hex string.

Once msg.data has been parsed (see example at right), the tickerA and tickerB values should reflect the asset types from the endpoint queried (in all capitals, "NEXA" always tickerA). The parsed epochSeconds value should match the epochSeconds field returned in the response from the http request. The parsed price value divided by 1e16 should match the price field returned in the response from the http request. Price units are specified per Nexa Satoshi.

VALIDATION OF PRICE ORACLE API's MSG.SIGNATURE:

Additional content here

The power of the Nexa
network and ecosystem, at
your fingertips.

Wally Wallet is a self custodial digital wallet empowering users to easily send, receive, and manage digital assets like $NEXA, tokens, and NFTs. With integrated NFT viewing, comprehensive transaction history, and specialised features like split a bill or multi-account support, Wally makes managing your digital world secure, and effortless.

Wally Wallet Screen 1Wally Wallet Screen 2Wally Wallet Screen 3Wally Wallet Screen 4