Track Bitcoin prices on a live graph with Python
You will need Python 3+ installed on your machine. Basic knowledge of Python, Flask and JavaScript will be helpful.
Graphs and charts are great for representing information in a clear and concise way. They can be used in various apps to show data to users when needed. Quickly changing data can be better represented using realtime graphs and charts as users can quickly see both current and historical data easily.
In this tutorial, we will be making use of Pusher Channels, Plotly and Flask to build a simple app for displaying the price of a Bitcoin in a realtime graph and bar chart.
Here is what the final app will look like:
Prerequisites
To follow along properly, basic knowledge of Python, Flask and JavaScript (ES6 syntax) is needed. You will also need to install Python and virtualenv locally.
Virtualenv is a tool that helps us create isolated Python environments. This makes it possible for us to install dependencies (like Flask) in an isolated environment, and not pollute our global packages directory. To install virtualenv:
pip install virtualenv
Setup and configuration
Installing Flask
As stated earlier, we will be developing using Flask, a web framework for Python. In this step, we will activate a virtual Python environment and install Flask for use in our project.
To activate a virtual environment:
mkdir realtime-graph
cd realtime-graph
virtualenv .venv
source .venv/bin/activate
Note: To activate the virtual environment on a Windows machine, you would need to enter the path to the activate file (in
.venv/Scripts
) in Powershell / command prompt.
To install Flask:
pip install flask
We will also need the Requests library. Let us install it now:
pip install requests
Setting up Pusher
Pusher Channels is a service that makes it easy for us to supercharge our web and mobile applications with realtime updates. We will be using it primarily for powering the realtime updates to our graphs. Head over to Pusher.com and register for a free account, if you don’t already have one.
Next, create a Channels app on the dashboard and copy out the app credentials (App ID, Key, Secret and Cluster), as we would be needing these in our app.
Now we can install the Pusher Python library to help our backend communicate with the Pusher service:
pip install pusher
Installing Plotly
Plotly is a tool that helps to easily display visual data on the web. According to their website:
Plotly creates leading open source tools for composing, editing, and sharing interactive data visualization via the Web.
We will be making use of Plotly to create our graph and chart. To install Plotly to our app:
pip install plotly
File and folder structure
Here is the folder structure for the app. We will only limit it to things necessary so as to avoid bloat. Let us go ahead and create the following files and folders:
├── realtime-graph
├── app.py
└── templates
└── index.html
The templates folder contains our index.html
template file, while app.py
will house all our server-side code.
Building the backend
Now, we can start writing server-side code to perform our various app functions. Our app will get the prices for Bitcoin every 10 seconds. We will use Pusher to broadcast an event, along with the new prices every time data is retrieved.
We will start by importing the needed modules, configuring the Pusher object, and initialising some needed variables:
# ./app.py
from flask import Flask, render_template
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from pusher import Pusher
import requests, json, atexit, time, plotly, plotly.graph_objs as go
# create flask app
app = Flask(__name__)
# configure pusher object
pusher = Pusher(
app_id='YOUR_APP_ID',
key='YOUR_APP_KEY',
secret='YOUR_APP_SECRET',
cluster='YOUR_APP_CLUSTER',
ssl=True
)
# define variables for data retrieval
times = []
currencies = ["BTC"]
prices = {"BTC": []}
In the code block above, first we import the needed modules and libraries, including the Pusher and Plotly libraries. We also configure the Pusher object with the credentials gotten from the Pusher dashboard. Remember to replace YOUR_APP_ID
and similar values with the actual values for your own app.
Some variables are also defined to hold values we will need later in our app. times
stores the values of the time when we retrieve price data in a list. currencies
holds the list of currencies we will be fetching data for. In our case, this is just BTC
but you can add any number of currencies you want. prices
is a dictionary that holds the list of prices for currency defined.
Next, let us define a simple route to serve our app’s view:
# ./app.py
# ...
@app.route("/")
def index():
return render_template("index.html")
In the code block above, we use Flask’s render_template()
function to serve index.html
from the ./templates
folder on the index route.
Now we can get to the core of our app, which is creating a function that will retrieve Bitcoin prices and then broadcast that data in graph and chart form. Let us update app.py
with the function:
# ./app.py
# ...
def retrieve_data():
# create dictionary for saving current prices
current_prices = {}
for currency in currencies:
current_prices[currency] = []
# append new time to list of times
times.append(time.strftime('%H:%M:%S'))
# make request to API and get response as object
api_url = "https://min-api.cryptocompare.com/data/pricemulti?fsyms={}&tsyms=USD".format(",".join(currencies))
response = json.loads(requests.get(api_url).content)
# append new price to list of prices for graph
# and set current price for bar chart
for currency in currencies:
price = response[currency]['USD']
current_prices[currency] = price
prices[currency].append(price)
# create an array of traces for graph data
graph_data = [go.Scatter(
x=times,
y=prices.get(currency),
name="{} Prices".format(currency)
) for currency in currencies]
# create an array of traces for bar chart data
bar_chart_data = [go.Bar(
x=currencies,
y=list(current_prices.values())
)]
data = {
'graph': json.dumps(list(graph_data), cls=plotly.utils.PlotlyJSONEncoder),
'bar_chart': json.dumps(list(bar_chart_data), cls=plotly.utils.PlotlyJSONEncoder)
}
# trigger event
pusher.trigger("crypto", "data-updated", data)
In the code block above, we define a function retrieve_data()
that does the following:
- Makes a request to a remote API to retrieve current Bitcoin prices. We make use of the CryptoCompare API and the Requests library.
- Generates traces for the graph and bar chart using the Plotly plotly.graph_objs.Scatter and plotly.graph_objs.Bar.
- Encode the graph and bar chart data as JSON using the Plotly Json Encoder.
- Trigger a
data-updated
event using the configuredpusher
object, broadcasting the needed data on thecrypto
channel.
We use the configured pusher
object for broadcasting events on specific channels. To broadcast an event, we use the trigger()
method with the following syntax:
pusher.trigger('a_channel', 'an_event', {'some': 'data'})
Note: You can find the docs for the Pusher Python library here.
Pusher also grants us the ability to trigger events on various types of channels including Public, Private and Presence channels. Read about them here.
Scheduling the data retrieval function
A major part of our app is making sure our data retrieval functions runs at a 10 second interval, so as to keep users updated of the most current prices. To create this schedule, we make use of AppScheduler. To install AppScheduler:
pip install apscheduler
The following piece of code will register the job and run our retrieve_data()
function every 10 seconds:
# ./app.py
# ...
# create schedule for retrieving prices
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job(
func=retrieve_data,
trigger=IntervalTrigger(seconds=10),
id='prices_retrieval_job',
name='Retrieve prices every 10 seconds',
replace_existing=True)
# Shut down the scheduler when exiting the app
atexit.register(lambda: scheduler.shutdown())
Running the Flask app
Finally, to start the app in debug mode, while disabling the auto reloader:
# ./app.py
# ...
# run Flask app
app.run(debug=True, use_reloader=False)
Note: We disable the auto reloader so as to prevent our scheduled function from running twice at every interval. You can read more on this here.
Creating our app view
We will now write some markup and JavaScript code to display the graph and chart to our users. Editing the index.html
file:
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CryptoLiveChart!</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
<style>
.chart {
height: 800px;
}
</style>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">Welcome to <strong>Crypto</strong>LiveChart!</h1>
<p class="subtitle">View live prices for <strong>Bitcoin</strong> and <strong>Ethereum</strong> in real time!</p>
<hr>
<div class="columns">
<div class="column">
<h5 class="title is-6">Prices (in USD)</h5>
<div id="price_chart" class="chart">
Graph
</div>
</div>
<div class="column">
<h5 class="title is-6">Market Cap</h5>
<div id="market_cap_chart" class="chart">
Bar Chart
</div>
</div>
</div>
</div>
</section>
</body>
</html>
The above code contains the basic markup for the homepage. We imported Bulma (a cool CSS framework) to take advantage of some pre-made styles.
Next, we will write some JavaScript code to display our charts:
<!-- ./templates/index.html -->
<!-- ... -->
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<!-- Plotly.js -->
<script src="https://d14fo0winaifog.cloudfront.net/plotly-basic.js"></script>
<!-- import Pusher-js library -->
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script type="text/javascript">
// connect to Pusher
const pusher = new Pusher('YOUR_APP_KEY', {
cluster: 'YOUR_APP_CLUSTER', // gotten from Pusher app dashboard
encrypted: true // optional
});
// subscribe to crypto channel
const channel = pusher.subscribe('crypto')
// listen for relevant events
channel.bind('data-updated', data => {
const graph = JSON.parse(data.graph);
Plotly.newPlot('price_chart', graph);
const bar_chart = JSON.parse(data.bar_chart);
Plotly.newPlot('market_cap_chart', bar_chart);
});
</script>
</body>
</html>
In the code above, first we import the needed libraries - D3.js, Plotly and Pusher. Then we do the following:
- Connect to our Pusher app by creating an instance with
new Pusher()
. Remember to replaceYOUR_APP_KEY
andYOUR_APP_CLUSTER
with the actual values gotten from your dashboard. - Subscribe to the
crypto
channel on which we will be broadcasting events withpusher.subscribe()
- We bind the
data-updated
event on the channel and define a callback function to plot our graph and chart anytime data is received.
And that’s it! To run our app:
python app.py
Conclusion
In this tutorial we have learned how to create beautiful realtime charts and graphs in our Flask Apps using Plotly and Pusher. You can check out the Plotly Python documentation to see how to create other graphs quickly in Python!
You can find the entire code for this tutorial on GitHub.
24 June 2018
by Olayinka Omole