Graphing Data with Python, MongoDB and Plotly


Cryptocurrency, specifically Ethereum and Bitcoin seem to be in the news a lot at the moment and due to their wild price movements, I thought it would be an interesting use case for generating some graphs using Plotly. So in this article, I'll utilise Python, to connect to the BTC Markets API, retrieve the various Cryptocurrency ticker statistics and write them to a local MongoDB instance; then finally create a line graph using the Plotly Python module.

Note, I'm not going to cover installing MongoDB, the article assumes a local instance running with all the defaults for simplicity.

Firstly install the required Python modules. Note, I'm using Python 3.5 on OSX so your command may vary slightly.

pip3.5 install pymongo requests plotly

Using the btc_dump.py script I've created below (and found here), will connect to the BTC Markets API and retrieve ticker data for Ethereum (ETH) and Bitcoin (BTC). The script is fairly straight forward using the Python requests module to perform an HTTP GET for the Ethereum and Bitcoin ticker API pages, then using the json.loads() method to deserialize the string data; finally the mongo_insert() function is called to insert the deserialized data into the MongoDB collection.

"""This script connects to the BTC Markets API
Downloads ticker data, and writes the data to a local MongoDB instance

Author: Jason Neurohr
Version: 0.1
"""

import json
import pymongo
import requests


def mongo_insert(rec):
    """Insert record into MongoDB
    """

    client = pymongo.MongoClient('localhost', 27017)
    db = client.btc
    collection = db['ticks']

    try:
        result = collection.insert_one(rec)
        rec_id = result.inserted_id
    except pymongo.errors.ConnectionFailure as err:
        print('Connection Error: ', err, sep='')

def get_tick(url):
    """Get tick data from BTCMarkets API

    Returns:
        dict: Tick data as a dictionary
    """

    req = requests.session()
    res = req.get(url)
    res_text = json.loads(res.text)

    return res_text

def main():
    """Main function
    """

    # Initialise ticker URLs list
    tick_urls = [
        'https://api.btcmarkets.net/market/ETH/AUD/tick',
        'https://api.btcmarkets.net/market/BTC/AUD/tick'
    ]

    # For each ticker URL in the tick_urls list
    # get the data and then commit to MongoDB
    for ticker in tick_urls:
        rec = get_tick(ticker)
        mongo_insert(rec)

if __name__ == "__main__":
    main()

Before using the script first create the MongoDB database, to do this run the mongo binary and enter the command use btc. This will create the database (and confirm mongod is running).

The BTC Markets API data is updated every minute, so to capture data points at regular intervals for graphing a cron entry can be used. For example, I have the following crontab entry:

* * * * * /usr/bin/python3.5 /home/jason/btc_dump.py

Now that cron is running the btc_dump.py script every minute we can move onto graphing the data using Plotly.

To do this a second script btc_graph.py, shown below (and available here) will

  • Connect to the MongoDB database
  • Perform a find on the collection filtering by ETH or BTC for Ethereum and Bitcoin documents respectively, and storing the results in various list objects
  • Construct a Plotly line graph in the btc.html file and automatically open a new browser tab to display it
"""This script connects to a local MongoDB instance and
generates a line Graph using Plotly for data inserted by the
btc_dump.py script

Author: Jason Neurohr
Version: 0.1
"""

import datetime
import plotly
import pymongo
from plotly.graph_objs import Layout, Scatter


def main():
    """Main Function
    """

    eth_last_price = []
    eth_best_bid = []
    eth_best_ask = []
    eth_timestamp = []

    btc_last_price = []
    btc_best_bid = []
    btc_best_ask = []
    btc_timestamp = []

    client = pymongo.MongoClient('localhost', 27017)
    db = client.btc
    collection = db['ticks']

    try:
        for rec in collection.find({"instrument":"ETH"}):
            eth_last_price.append(rec['lastPrice'])
            eth_best_bid.append(rec['bestBid'])
            eth_best_ask.append(rec['bestAsk'])
            eth_ts = datetime.datetime.fromtimestamp(rec['timestamp'])
            eth_timestamp.append(eth_ts)
    except Exception as err:
        print(err)

    # Because of the price difference between Etherum and Bitcoin
    # Divide Bitcon prices by 10 to make the graph nicer
    try:
        for rec in collection.find({"instrument":"BTC"}):
            btc_last_price.append(rec['lastPrice'] / 10)
            btc_best_bid.append(rec['bestBid'] / 10)
            btc_best_ask.append(rec['bestAsk'] / 10)
            btc_ts = datetime.datetime.fromtimestamp(rec['timestamp'])
            btc_timestamp.append(btc_ts)
    except Exception as err:
        print(err)

    eth_lp = Scatter(
        y=eth_last_price,
        x=eth_timestamp,
        name='ETH Last Price',
        mode='lines+markers')

    eth_bb = Scatter(
        y=eth_best_bid,
        x=eth_timestamp,
        name='ETH Best Bid',
        mode='lines+markers')

    eth_ba = Scatter(
        y=eth_best_ask,
        x=eth_timestamp,
        name='ETH Best Ask',
        mode='lines+markers')

    btc_lp = Scatter(
        y=btc_last_price,
        x=btc_timestamp,
        name='BTC Last Price minus 10^1',
        mode='lines+markers')

    btc_bb = Scatter(
        y=btc_best_bid,
        x=btc_timestamp,
        name='BTC Best Bid minus 10^1',
        mode='lines+markers')

    btc_ba = Scatter(
        y=btc_best_ask,
        x=btc_timestamp,
        name='BTC Best Ask minus 10^1',
        mode='lines+markers')

    plotly.offline.plot(
        {
            'data': [eth_lp, eth_bb, eth_ba, btc_lp, btc_bb, btc_ba],
            'layout': Layout(title="BTC Markets Graph")},
        filename=r"btc.html", auto_open=True)

if __name__ == "__main__":
    main()

Similarly to before, a cron job can be used to automate things such that the btc.html file can be updated every one minute and served from a web server, for example, I have done so here: here. On this page, I've created a very simple PHP file that contains several iframes linked to various Plotly HTML graph files being generated every minute.