Oracle Prices & API Information

...

Logo 1 Logo 2 Logo 3 Logo 4 Logo 5

Daily Volume-Weighted Average Price Reference:

Price calculated daily at 00:05:00 UTC

Trade data (prices, volume) collected from BingX, BitGet, BitMart, Coinex and Mexc.

For each one-hour interval beginning from 00:00 UTC until 23:59:59 UTC every day, all trades are first ordered by price. The total volume of the interval is summed. Next, the first and last quartiles of volume are removed, leaving the middle 50% of volume prices. If a trade straddles the upper- or lower-quartile threshold, its volume is split - so that 50% of total volume is always achieved. For each trade, price is multiplied by volume, and then all are summed and divided by the 50% total volume. This gives a volume weighted hourly average price. Lastly, the arithmetic mean of all hourly averages is taken. If there exists any hour for which there is no volume, for consistency, the daily price will not be calculated. This approach combines the benefits of:

  • TWAP (time-weighted average price)
  • volume-weighting
  • median price-filtering

...

Logo 1 Logo 2 Logo 3 Logo 4 Logo 5

Hourly Volume-Weighted Average Price Reference:

Price calculated hourly at {hour}:05:00 UTC

Trade data (prices, volume) collected from BingX, BitGet, BitMart, Coinex, and Mexc.

For each fifteen-minute interval beginning from xx:00 until xx:59:59 every hour, all trades are first ordered by price. The total volume of the interval is summed. Next, the first and last quartiles of volume are removed, leaving the middle 50% of volume prices. If a trade straddles the upper- or lower-quartile threshold, its volume is split - so that 50% of total volume is always achieved. For each trade, price is multiplied by volume, and then all are summed and divided by the 50% total volume. This gives a volume weighted quarterly average price. Lastly, the arithmetic mean of all quarterly averages is taken. If there exists any quarter for which there is no volume, for consistency, the hourly price will not be calculated. This approach combines the benefits of:

  • TWAP (time-weighted average price)
  • volume-weighting
  • median price-filtering

...

Logo 1 Logo 2 Logo 3 Logo 4 Logo 5

SELECT TIME PARAMETERS, THEN CLICK 'GET PRICE'!

 

Oracle Prices Historic Lookup

Ticker A Ticker B Price Type Year Month Day Time Begin (UTC) Time End (UTC) Price
NEXA  

** prices available beginning 2024-07-23 @ 17:00:00 UTC

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": Object of
                                        {
                                          "data": String (32 byte hex)
                                          "signature": String (64 byte hex)
                                        },
                                      "epochSeconds": Long,
                                      "price": Decimal number in a String,
                                      "pairPriceUnit": String
                                    }
                                    
Example response:

...

EXAMPLE REQUEST QUERY STRINGS:

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 ParsedData(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