Blitz Ticker Documentation

Realtime Rate Data on Bitcoin Cash

Learn how to consume Blitz Ticker Here

Introduction

Blitz Ticker provides a decentralized, always available rate publishing system. Rate data is encoded into Bitcoin Cash transactions and propagated throughout the world. This document will show you how you can start consuming this data in your own applications.

For the following examples We will be using Fountainhead.cash BitDB which is also what this website uses for querying data. If you are unfamiliar with the syntax do read over the docs. It is not required to use BitDB for querying the data, however for the sake of simplicity it will be used here.

Address

Blitz Ticker uses addresses which each map to a separate set of symbols. These are stored at:

https://blitzticker.com/addrmap/{ADDRESS}.json
Let's take a look at the first example:
{
    "symbols": [
        {"symbol": "XBTUSD", "provider": "BitMEX", "description": "" },
        {"symbol": "ADAM19", "provider": "BitMEX", "description": "" },
        {"symbol": "BCHM19", "provider": "BitMEX", "description": "" },
        {"symbol": "EOSM19", "provider": "BitMEX", "description": "" },
        {"symbol": "ETHM19", "provider": "BitMEX", "description": "" },
        {"symbol": "LTCM19", "provider": "BitMEX", "description": "" },
        {"symbol": "TRXM19", "provider": "BitMEX", "description": "" },
        {"symbol": "XRPM19", "provider": "BitMEX", "description": "" }
    ]
}
As you can see this has a list of 8 symbols; each with a symbol name, and some metadata describing these fields. This format is forever associated with this address, so downloading and storing it is completely safe.

The most important part is the mapping of symbol names and their order, the data in the format shown below will correspond to the order inside this list.

  1. XBTUSD
  2. ADAM19
  3. BCHM19
  4. EOSM19
  5. ETHM19
  6. LTCM19
  7. TRXM19
  8. XRPM19

Format

The rate data is stored in the following format:

OP_RETURN OP_PUSHDATA [TIMESTAMP] OP_PUSHDATA [DATA]
The timestamp is 32bit Unix Time. The blob contains multiple Single-precision floating-point format data pieces, each representing the current price of a symbol.

Querying

This will include just a small introduction on how to query data from Fountainhead BitDB. We use Bitquery format to send requests to the BitDB endpoint.

The following is an example query to get the 10 most recent transactions from the Blitz Ticker address above. [view results]

const query = {
    "v": 3,
    "q": {
        "find": {
            "in.e.a": "qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq"
        },
        "limit": 10
    }
};
const b64 = btoa(JSON.stringify(query));
const url = "https://bitdb.fountainhead.cash/q/" + b64;

fetch(url)
.then(r => r.json())
.then(data => console.log(data));

However this by itself is not very useful, so let us design a more useful query.

Grabbing Historical Rate Data

This query will search for data between the timestamps (of the blocks) provided, and format them to be useful for your application. [view results]

{
  "v": 3,
  "q": {
    "aggregate": [
      {
        "$match": {
          "$and": [
            {
              "in.e.a": "qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq"
            },
            {
              "blk.t": {
                "$gte": 1554679790
              }
            }
          ]
        }
      },
      {
        "$unwind": "$out"
      },
      {
        "$match": {
          "out.b0.op": 106
        }
      },
      {
        "$sort": {
          "blk.i": 1
        }
      }
    ],
    "limit": 100
  },
  "r": {
    "f": "[ .[] | { txid: .tx.h, blk_height: .blk.i, timestamp: .out.h1, data: .out.h2 } ] | sort_by(.timestamp)"
  }
}

This might take a little more time to digest than the previous query, but to summarize the steps:

  • Perform an aggregate query.
  • Filters only the transactions involving the address qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq
  • Filters transactions to only be between blocks whose timestamp is greater than 1554679790.
  • Explodes the out array (outputs) so we can query on each of them.
  • Filters only the exploded items whose output is OP_RETURN 106.
  • Sorts the blocks by their height (blk.i).
  • Limits the amount of rows to 100.
  • Formats the data using jq.

Grabbing Realtime Data

For this we will use Server Side Events while connected to a Fountainhead Bitsocket node.

To set this up we must initialize a SSE connection. To do so with Javascript we can use the following code:

const query = {}; // we want this to be a valid bitquery
const b64 = btoa(JSON.stringify(query));
const url = "https://bitsocket.fountainhead.cash/s/" + b64;

let sse = new EventSource(url);
sse.onmessage = (e) => console.log(JSON.parse(e.data));

This code will allow us to substitute the query and get real-time ticker updates. Below shows what is a standard way to query for this data. [view results]

{
  "v": 3,
  "q": {
    "find": {
      "in.e.a": "qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq"
    }
  },
  "r": {
    "f": "[ .[] | {timestamp: .out[] | select(.b0.op == 106) | .h1, data: .out[] | select(.b0.op == 106) | .h2, txid: .tx.h} ][0]"
  }
}

Now let's break this apart. First we filter all transactions which are not qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq. Secondly, we perform a jq transformation which extracts the timestamp and data from the only output which is OP_RETURN.

Extracting Encoded Data

Now that we have our data pipeline set up, it is time to extract. Fortunately, because of the simple design of Blitz Ticker, this should be quite simple regardless of the platform.

Imagine we have data which looks like this:

{
  "timestamp": "5caaad79",
  "data": "45a6a00037944f833d80d1b73a8f42ff3d1522a73c95421c36bb91bc389642cd",
  "txid": "4dbbb6631e67529a2bfc12eca67241baf282a7b58c56ef456facb90f6fe9b4aa"
}

We will want to end up with data which looks like this:

{
    "timestamp": 1554689401,
    "data": [
        {
            "symbol": "XBTUSD",
            "value": 5332.000147342699
        },
        {
            "symbol": "ADAM19",
            "value": 0.000017680000671455716
        },
        {
            "symbol": "BCHM19",
            "value": 0.06289999936819068
        },
        {
            "symbol": "EOSM19",
            "value": 0.001093000071033844
        },
        {
            "symbol": "ETHM19",
            "value": 0.03641000079035769
        },
        {
            "symbol": "LTCM19",
            "value": 0.018220000278353726
        },
        {
            "symbol": "TRXM19",
            "value": 0.000005590000212298499
        },
        {
            "symbol": "XRPM19",
            "value": 0.00007165000114701702
        }
    ]
}

To do this we have multiple steps:

1. Get Address Map

We should cache this by saving it locally since the symbols will not change.

fetch('https://blitzticker.com/addrmap/qqrxa0h9jqnc7v4wmj9ysetsp3y7w9l36u8gnnjulq.json')
.then(r => r.json())

// let's save this data to a variable for ease of reading

const addr_map = [
    {
        "symbol": "XBTUSD",
    },
    {
        "symbol": "ADAM19",
    },
    {
        "symbol": "BCHM19",
    },
    {
        "symbol": "EOSM19",
    },
    {
        "symbol": "ETHM19",
    },
    {
        "symbol": "LTCM19",
    },
    {
        "symbol": "TRXM19",
    },
    {
        "symbol": "XRPM19",
    }
];

Also, let's use the data from a historical query for our example as well:

let historical_data = [
    {
        "timestamp": "5caa8766",
        "data": "45a2e400379652e83d82a9933a8d4bad3d0d64d83c95feda36bd3f3b3897cab7"
    },
    {
        "timestamp": "5caa8769",
        "data": "45a2fc00379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    },
    {
        "timestamp": "5caa876b",
        "data": "45a2f800379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    },
    {
        "timestamp": "5caa876c",
        "data": "45a2f800379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    }
];

2. Convert Timestamp

We must transform our historical data, first let's start with timestamp:

historical_data = historical_data.map(v => ({
    timestamp: Number("0x" + v.timestamp), // convert hex to decimal
    data: v.data
}))

// results in:

[
    {
        "timestamp": 1554679654,
        "data": "45a2e400379652e83d82a9933a8d4bad3d0d64d83c95feda36bd3f3b3897cab7"
    },
    {
        "timestamp": 1554679657,
        "data": "45a2fc00379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    },
    {
        "timestamp": 1554679659,
        "data": "45a2f800379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    },
    {
        "timestamp": 1554679660,
        "data": "45a2f800379652e83d82a9933a8d4bad3d0d64d83c95aaf836bd3f3b3897cab7"
    }
]

3. Extract Rate Data

Next we will extract the data from the blob, this involves splitting the data apart, pairing the symbol name with the corresponding piece of the data, and converting the encoded value back to a single-precision floating-point number.

// this converts a 32 bit integer into a single-precision floating-point number
const hex2float = (num) => {
    const sign = (num & 0x80000000) ? -1 : 1;
    const exponent = ((num >> 23) & 0xff) - 127;
    const mantissa = 1 + ((num & 0x7fffff) / 0x7fffff);
    return sign * mantissa * Math.pow(2, exponent);
};

historical_data = historical_data.map(v => ({
    timestamp: v.timestamp,
    data: v.data.match(/.{8}/g) // split every 4 bytes or 8 hex characters
          .map((v, i) => ({
              symbol: addr_map[i].symbol, // remember the importance of ordering from above?
              value:  hex2float(Number("0x" + v)) // convert our hex string to a decimal number and then to a float
          }))
}));

// results in...

[
    {
        "timestamp": 1554679654,
        "data": [
            {
                "symbol": "XBTUSD",
                "value": 5212.500133097188
            },
            {
                "symbol": "ADAM19",
                "value": 0.000017919999980959107
            },
            {
                "symbol": "BCHM19",
                "value": 0.06379999980926511
            },
            {
                "symbol": "EOSM19",
                "value": 0.0010779999550282902
            },
            {
                "symbol": "ETHM19",
                "value": 0.034520000609159544
            },
            {
                "symbol": "LTCM19",
                "value": 0.018309999577403018
            },
            {
                "symbol": "TRXM19",
                "value": 0.000005640000144236272
            },
            {
                "symbol": "XRPM19",
                "value": 0.00007237999806147046
            }
        ]
    },
    {
        "timestamp": 1554679657,
        "data": [
            {
                "symbol": "XBTUSD",
                "value": 5215.500133454816
            },
            {
                "symbol": "ADAM19",
                "value": 0.000017919999980959107
            },
            {
                "symbol": "BCHM19",
                "value": 0.06379999980926511
            },
            {
                "symbol": "EOSM19",
                "value": 0.0010779999550282902
            },
            {
                "symbol": "ETHM19",
                "value": 0.034520000609159544
            },
            {
                "symbol": "LTCM19",
                "value": 0.018270001130700247
            },
            {
                "symbol": "TRXM19",
                "value": 0.000005640000144236272
            },
            {
                "symbol": "XRPM19",
                "value": 0.00007237999806147046
            }
        ]
    },
    {
        "timestamp": 1554679659,
        "data": [
            {
                "symbol": "XBTUSD",
                "value": 5215.000133395211
            },
            {
                "symbol": "ADAM19",
                "value": 0.000017919999980959107
            },
            {
                "symbol": "BCHM19",
                "value": 0.06379999980926511
            },
            {
                "symbol": "EOSM19",
                "value": 0.0010779999550282902
            },
            {
                "symbol": "ETHM19",
                "value": 0.034520000609159544
            },
            {
                "symbol": "LTCM19",
                "value": 0.018270001130700247
            },
            {
                "symbol": "TRXM19",
                "value": 0.000005640000144236272
            },
            {
                "symbol": "XRPM19",
                "value": 0.00007237999806147046
            }
        ]
    },
    {
        "timestamp": 1554679660,
        "data": [
            {
                "symbol": "XBTUSD",
                "value": 5215.000133395211
            },
            {
                "symbol": "ADAM19",
                "value": 0.000017919999980959107
            },
            {
                "symbol": "BCHM19",
                "value": 0.06379999980926511
            },
            {
                "symbol": "EOSM19",
                "value": 0.0010779999550282902
            },
            {
                "symbol": "ETHM19",
                "value": 0.034520000609159544
            },
            {
                "symbol": "LTCM19",
                "value": 0.018270001130700247
            },
            {
                "symbol": "TRXM19",
                "value": 0.000005640000144236272
            },
            {
                "symbol": "XRPM19",
                "value": 0.00007237999806147046
            }
        ]
    }
]

There we go. The same method can be applied to realtime data as well just as easily.

Help!

If any of this isn't clear, feel free to join the Fountainhead Cash chat where people can help you with BitDB queries. In addition, it might be useful to look at the source code of this site.

If there are any questions or suggestions outside of that you should contact the Blitz Group who may assist you. Happy hacking!