Real-time stock price alerts using Python, Grafana and QuestDB

A laptop showing a graph of financial data over time
Photo by Tech Daily via Unsplash

This submission comes from one of our community contributors Kovid Rathee who has written a great guide for setting up alerting via Grafana.

Thanks for your contribution, Kovid!

Introduction#

There are many reasons why reacting to time series data is useful, and the quicker you can respond to changes in this data, the better. The best tool for this job is easily a time series database, a type of database designed to write and read large amounts of measurements that change over time.

In this tutorial, you will learn how to read data from a REST API and stream it to QuestDB, an open-source time series database. We will use Grafana to visualize the data and notify Slack of changes that interest us. We use Python to fetch data from the API and stream it to QuestDB, and you can easily customize the scripts to check different stocks or even APIs.

Prerequisites#

Before getting started with the tutorial, you will need the following:

This tutorial uses Slack as an example notification channel to deliver alerts via Grafana, but it's simple to choose another channel you would like alerts delivered to, such as your own REST API via webhook, Kafka, email, Pagerduty and more. For more details on the available notification channels, see the Grafana documentation for the list of supported notifiers.

Start QuestDB and Grafana#

To clone the GitHub repository and start the example project:

git clone https://github.com/questdb/questdb-slack-grafana-alerts.git
cd questdb-slack-grafana-alerts
docker-compose up

Running docker-compose up brings up two networked containers:

Running docker-compose up will also provision Grafana with the default connection credentials to QuestDB for Postgres authentication. By provisioning credentials, you can use QuestDB as a default data source in Grafana right away without manual configuration steps.

Verify QuestDB and Grafana are running by visiting the URLs listed above or use docker-compose ps from the repository root which will show you the running containers:

docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------------------------------------
grafana_alerts /run.sh Up 0.0.0.0:3000->3000/tcp
questdb_alerts /app/bin/java --add-export ... Up 0.0.0.0:8812->8812/tcp, 0.0.0.0:9000->9000/tcp, 9009/tcp

Send data to QuestDB#

The Python dependencies required for this tutorial are provided in the requirements.txt file and can be installed using pip:

pip install -r requirements.txt

We will use the IexFinance API to fetch real-time stock prices, but first, we can verify the configuration is correct by using a test script that generates dummy data. The mock script generates random prices so that we don't deplete the API call limit during testing.

To start sending mock data to QuestDB, run the mock data script in the python directory:

cd python
python mock_stock_data_example.py

The script will create a table stock_prices, and it will start sending mock data to this table. The columns that we have are:

columndescription
stockListedname of the stock, e.g., TSLA for Tesla
stockPriceprice of the stock in USD as double
createdDateTimetimestamp at which stockPrice was ingested in QuestDB

One feature of QuestDB that we are using for the stock name is the symbol data type optimized for text columns with repetitive values. More information on this type can be found on the QuestDB documentation for symbol type.

To verify if data is arriving in QuestDB:

  1. Navigate to localhost:9000
  2. Run the following query:
SELECT * FROM stock_prices;

We should see all rows from our table returned at the bottom panel:

A screenshot of the QuestDB web console displaying rows of a database table

Query real-time stock prices#

Once you have tested the ingestion using the mock data script, we can start making requests to an API for live market data and query real-time stock prices. To configure IexFinance API authentication:

  1. On the IexFinance console, create an API token
  2. Click Reveal Secret Token and copy the value
A screenshot of the IexFinance console with API keys

Now we can add this token to our project, so the Python scripts have programmatic access to the IexFinance API. To store project secrets, we are using the Python dotenv package, which allows passing configuration to Python scripts as environment variables. We can explicitly pass a token to each API call to IexFinance, or we can use the IEX_TOKEN environment variable that the Python library will check implicitly. To add the token:

  1. Create a new file ./python/.env
  2. Paste the token in the .env file in the format IEX_TOKEN=Skwf93hD...
  3. Run the live market data example:
cd python
python stock_data_TSLA_example.py

If the markets are open, real-time prices will be sent to QuestDB:

Inserting rows into table 'stock_prices' - press Ctrl-C to stop
Inserting into 'stock_prices': TSLA 673.58 2021-03-10 12:37:23.147258
...

Slack incoming webhook#

The next step is to create an incoming webhook in Slack to send alerts as HTTP(S) requests from Grafana. To set up the webhook:

  1. Navigate to the create a new Slack app page and create an app called Price Alerts for your workspace
  2. Click Incoming Webhooks in the features section
  3. Activate incoming webhooks and click Add New Webhook to Workspace
  4. Select the Slack channel you want to receive alerts in and click Allow
  5. Copy the Webhook URL which is in the following format
https://hooks.slack.com/services/T123/B0123/2Fb...

Grafana notification channels#

The final step in connecting Slack and Grafana is to create a notification channel. To configure a Slack notification channel to send alerts to:

  1. Log in to Grafana at localhost:3000 using the default credentials:
user:admin
pass:admin
  1. Navigate to notification channels and click Add channel
  2. Give it the name Stock Price Alert and choose the Slack type
  3. Paste the webhook URL in the Url field
  4. In the Notification settings dropdown, enable the Include image checkbox
  5. Click Test to verify that the configuration is correct and then click Save

Your Slack workspace should display a test notification coming from Grafana:

A test Slack notification from Grafana with example error information and a chart

Grafana alerts#

Next up, we can create a new panel on Grafana and configure alerts based on queries we're interested in:

  1. Navigate to Create new Dashboard and click + Add new panel
  2. In the Query panel, click the pencil icon or click Edit SQL
  3. Paste the following example query
SELECT createdDatetime time,
round(avg(stockPrice),2) avgPrice
FROM stock_prices
WHERE stock = 'TSLA'
SAMPLE BY 5s;

We now have a visualization of five-second averages of Tesla's stock price:

A dashboard in Grafana showing Tesla stock price over time

To make sure we don't lose our visualization panel, click Save and give the dashboard a name of our choice.

Rules and conditions#

To understand how alerts work, let's take a brief look at the concepts. The two main components for setting up alerts are the alert Rule and Conditions.

The Rule has the following settings:

  • Name to give the alert a descriptive title
  • Evaluate every is how often the scheduler will evaluate the alert rule
  • for specifies how long the query needs to violate the thresholds before triggering alert notifications

The Conditions section has the following settings:

  • WHEN sets an aggregate function on a series
  • OF is the query to alert on over a time range in the format query(query_name, from, until)
  • IS ... allows specifying a comparison to a value or range

For more information on the conditions of alerting, see the Grafana alert conditions documentation.

Create an alert#

The example alert we are using in this tutorial will be triggered if the minimum value of the query named 5-second Avg. of TSLA is below 762, and we will set the time range for alerting to the last thirty seconds. To set up this alert:

  1. Edit the query and select the Alert tab
  2. Click Create Alert and give it the name Tesla Stock Price alert
  3. In the Rule section:
    • Set Evaluate every to 10 seconds
    • Set for to 30 seconds
  4. In the Conditions section:
    • Set WHEN to min()
    • Set OF to query(5-second Avg. of TSLA, 30s, now())
    • Click IS ABOVE, change this to IS BELOW, and set the value to 762
  5. Use the default values in the No Data & Error Handling section
  6. In Notifications -> Send to, choose the Stock Price Alerts notification channel
  7. Add an optional message to be delivered with the alert
Configuration settings for alerting in Grafana based on Tesla stock prices over the last 30 seconds

When the conditions of the alert are met, we will see Slack notifications arriving in the channel that we have configured:

A Slack notification displaying an alert from Grafana that Tesla stock price has exceeded a threshold

Summary#

In this tutorial, we've shown how Grafana and QuestDB can be quite powerful for data visualization and alerting. We learned how to ingest live ticker data from a REST API into QuestDB and how to visualize this data in a Grafana dashboard and set up alerts based on predefined conditions.

The next steps for improvements to the Python scripts could be command-line arguments for multiple stock symbols, and better error handling if markets are not open. Feel free to submit a PR if you have a suggestion or improvements to make!

If you like this content, we'd love to know your thoughts! Feel free to share your feedback or just come and say hello in the QuestDB Community Slack.