top of page

Creating a Crypto Dashboard with Custom CSS in Streamlit

  • Writer: Nikhil Adithyan
    Nikhil Adithyan
  • Nov 5, 2023
  • 8 min read

An easy approach to truly harness the power of streamlit and create great-looking dashboards



Dashboards are a great way to represent any kind of data and it’s one such form to make great sense of the data we are dealing with. Building stunning dashboards using HTML, CSS, and JavaScript frameworks can be a very tricky job especially when you want to add complex functionalities and intricate designs to the dashboard. But fear not, Python’s here for your rescue.


With the introduction of a massive number of frameworks and libraries dedicated to web development, Python is becoming a favorite among web developers. Today, we will be using one such powerful framework called Streamlit to build our great-looking, professional crypto dashboard.


This article contains a lot of things like acquiring the crypto data using TAAPI.IO’s API endpoints, defining the dashboard layout, adding custom CSS to the dashboard, etc. So fasten your seatbelts for this exciting ride!


Acquiring the Data

Before starting to build the dashboard, it is essential to have a clear background on the data we are going to work with. Let’s start with importing the essential packages into our Python environment:


# IMPORTING PACKAGES

import streamlit as st
import requests
import pandas as pd

If you haven’t installed any of the imported packages, make sure to do so using the pip command in your terminal.


Now, we are going to pull the crypto data from one of the most reliable data providers in the market, TAAPI.IO. They offer both real-time and historical crypto as well as stock data API endpoints. Along with that, their unique and huge library of technical indicators API comes in handy for tasks like algo trading and much more. Make sure to create a TAAPI.IO account for the smooth execution of all the upcoming code.


In this article, our focus is only on two of their crypto APIs which are Real-Time Price API, and Historical Crypto Prices API. Starting off with the real-time API, here’s an example of using it in Python:


api_key = 'YOUR API KEY'
btc_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=BTC/USDT&interval=1m').json()
btc_price

Pretty simple, right? Now, let’s analyze the API URL to understand more about the endpoint. It takes the API key, the exchange from which we are interested in getting the data, the crypto symbol, and the interval/timeframe of the data. Apart from these, there are a lot of additional parameters for further filtering the data which you can view in their documentation. The output is a JSON response that looks like this:


{'value': 33999.99}

Another API that we’ll be using for our dashboard is the historical crypto data API which, as the name suggests, helps in obtaining historical data for a given cryptocurrency. The following code uses this API to extract Bitcoin’s historical data for the last one year:


api_key = 'YOUR API KEY'
url = f'https://api.taapi.io/candles?secret={api_key}&exchange=binance&symbol=BTC/USDT&interval=1d&period=365'
hist_json = requests.get(url).json()
hist_df = pd.DataFrame(hist_json).drop('timestampHuman', axis = 1).rename(columns = {'timestamp':'time'})
hist_df.time = pd.to_datetime(hist_df.time, unit = 's')
hist_df.tail()

The code is pretty much the same except for some data manipulation tasks. The parameters of the API are very similar to those of the real-time API but one additional parameter we are adding here is the time period of the historical data (365 days in our case). This is the final output dataframe:



Awesome! Now that we have a good background on the two APIs we’re going to use to power the dashboard, let’s start with our actual work of building it.


Sketching the Layout

This is one of the most overlooked steps but can be of incredible help and saves a ton of time. Having some sort of reference material for designing the dashboard exponentially boosts the development process. Here’s a rough sketch of the dashboard we’re going to build:



Nothing fancy here but just a plain, simple, and clean design. The header consists of the dashboard title along with five cards that reveal the real-time prices of five major crypto pairs.


As the body, we have three sections. The first is the chart parameters which contain streamlit widgets like selectbox and number input. The second section is dedicated to charting the historical data using a Python library for creating TradingView charts. The third and final section is for representing the historical data in table format.


Each part of the dashboard will be designed with custom CSS as Streamlit’s preset designs are too generic and basic. With that being said, let’s start building the dashboard in Streamlit.


Building Blocks: Creating Dashboard.py, style.css, config.toml

Before jumping into building the streamlit app, it is highly recommended to keep things in place as it is a much more organized way of development.


To start off, create a Dashboard.py (name the file as you like) which will be the core file for running the streamlit app. This is the file where the actual building of the app undergoes.


Secondly, since we are going to build the app with custom CSS, it is mandatory to have a separate CSS file. You can name the file as you like but I prefer a more common naming convention, i.e., style.css. In this file, we will be customizing all the pre-defined streamlit CSS properties.


Finally, in order to change the theme of the streamlit app globally, we have to create a separate directory with the name of .streamlit inside which, we need to have a config.toml file. After creating the TOML file, copy and paste the following code to replicate the theme of the dashboard:


[theme]
primaryColor="#2962ff"
backgroundColor="#131722"
secondaryBackgroundColor="#0c0e15"
textColor="#f6f6f6"

The theme is a blend of dark mode with some blues in it for a pleasant and soft-looking feel. You can customize the theme as per your wish.


Building the Dashboard Header

Let’s take the final dashboard header as the reference material which will make it easier for me to explain the development process.



After analyzing the header section, here’s the approach we can follow.


Development Approach:

  • Specify the layout of the streamlit app to be wide

  • Creating seven columns with uneven spacing

  • Use st.markdown for the dashboard title instead of st.header since the former allows custom fonts and CSS

  • Use the real-time API with the timeframe as 1m to get live prices of the crypto pairs

  • Use st.container and st.markdown to create the unique price cards

Styling Approach:

  • Import custom font (Space Grotesk in our case) from Google Fonts

  • Customize the pre-defined padding of the streamlit app

  • Create a class for the dashboard title with custom font size, font weight, padding, and line height

  • Customize the CSS of st.container to include custom border, border radius, padding, and box shadow.

  • Create separate classes for each crypto pair title for unique colors.

  • Create a separate class for showing the real-time price of the crypto pairs which stays the same for all the price cards.

The following two blocks of code implement the two approaches we discussed above to create the header section of the dashboard. The first code block is written in the Dashboard.py file and the second on style.css.


Dashboard.py:


import streamlit as st
import requests
import pandas as pd
from lightweight_charts.widgets import StreamlitChart

if "symbols_list" not in st.session_state:
    st.session_state.symbols_list = None
    
st.set_page_config(
    layout = 'wide',
    page_title = 'Crypto Dashboard'
)

st.markdown(
    """
    <style>
        footer {display: none}
        [data-testid="stHeader"] {display: none}
    </style>
    """, unsafe_allow_html = True
)

with open('style.css') as f:
    st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html = True)

api_key = 'YOUR TAAPI.IO API KEY'
        
title_col, emp_col, btc_col, eth_col, xmr_col, sol_col, xrp_col = st.columns([1,0.2,1,1,1,1,1])

with title_col:
    st.markdown('<p class="dashboard_title">Crypto<br>Dashboard</p>', unsafe_allow_html = True)

with btc_col:
    with st.container():
        btc_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=BTC/USDT&interval=1m').json()['value']
        st.markdown(f'<p class="btc_text">BTC / USDT<br></p><p class="price_details">{btc_price}</p>', unsafe_allow_html = True)

with eth_col:
    with st.container():
        eth_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=ETH/USDT&interval=1m').json()['value']
        st.markdown(f'<p class="eth_text">ETH / USDT<br></p><p class="price_details">{eth_price}</p>', unsafe_allow_html = True)

with xmr_col:
    with st.container():
        xmr_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=XMR/USDT&interval=1m').json()['value']
        st.markdown(f'<p class="xmr_text">XMR / USDT<br></p><p class="price_details">{xmr_price}</p>', unsafe_allow_html = True)

with sol_col:
    with st.container():
        sol_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=SOL/USDT&interval=1m').json()['value']
        st.markdown(f'<p class="sol_text">SOL / USDT<br></p><p class="price_details">{sol_price}</p>', unsafe_allow_html = True)

with xrp_col:
    with st.container():
        xrp_price = requests.get(f'https://api.taapi.io/price?secret={api_key}&exchange=binance&symbol=XRP/USDT&interval=1m').json()['value']
        st.markdown(f'<p class="xrp_text">XRP / USDT<br></p><p class="price_details">{xrp_price}</p>', unsafe_allow_html = True)
        

style.css:


@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');

/* DASHBOARD PADDING */
div.block-container.css-z5fcl4.egzxvld4 {
    width: 100%;
    min-width: auto;
    max-width: initial;
    padding-left: 5rem;
    padding-right: 5rem;
    padding-top: 15px;
    padding-bottom: 40px;
}

/* GLOBAL FONT CHANGE */
html, body, [class*="css"] {
    font-family: 'Space Grotesk'; 
}

.st-ae {
    font-family: 'Space Grotesk';
}

/* CONTAINER CSS */
[data-testid="stVerticalBlock"] > [style*="flex-direction: column;"] > [data-testid="stVerticalBlock"] {
    border: 1px groove #52546a;
    border-radius: 10px;
    padding-left: 25px;
    padding-top: 10px;
    padding-bottom: 10px;
    box-shadow: -6px 8px 20px 1px #00000052;
}

/* CUSTOM MARKDOWN CLASSES */
.dashboard_title {
    font-size: 35px; 
    font-family: 'Space Grotesk';
    font-weight: 700;
    line-height: 1.2;
    text-align: left;
    padding-bottom: 35px;
}

.price_details {
    font-size: 30px; 
    font-family: 'Space Grotesk';
    color: #f6f6f6;
    font-weight: 900;
    text-align: left;
    line-height: 1;
    padding-bottom: 10px;
}

.btc_text {
    font-size: 14px; 
    font-family: 'Space Grotesk';
    color: #f7931a;
    font-weight: bold;
    text-align: left;
    line-height: 0.2;
    padding-top: 10px;
}

.eth_text {
    font-size: 14px; 
    font-family: 'Space Grotesk';
    color: #a1a1a1;
    font-weight: bold;
    text-align: left;
    line-height: 0.2;
    padding-top: 10px;
}

.xmr_text {
    font-size: 14px; 
    font-family: 'Space Grotesk';
    color: #ff6b08;
    font-weight: bold;
    text-align: left;
    line-height: 0.2;
    padding-top: 10px;
}

.sol_text {
    font-size: 14px; 
    font-family: 'Space Grotesk';
    color: #807af4;
    font-weight: bold;
    text-align: left;
    line-height: 0.2;
    padding-top: 10px;
}

.xrp_text {
    font-size: 14px; 
    font-family: 'Space Grotesk';
    color: #01acf1;
    font-weight: bold;
    text-align: left;
    line-height: 0.2;
    padding-top: 10px;
}
    

In the Dashboard.py, make sure to replace YOUR TAAPI.IO API KEY with your secret API key. In both files, there will be some stuff that goes beyond what we discussed in the development and styling approaches, but I’m not going to delve into the details as it will take a long time to explain them.


Building the Dashboard Body

Similar to what we did in the previous section, we’ll take the final output as the reference material to lay out the development and styling approaches. The following image is the final result of the dashboard’s body which we’re trying to build:



Development Approach:

  • Create three columns with unique spacing

  • Use st.form to create the parameters section

  • Create a container for accommodating the chart

  • Use the lightweight_charts library to create the interactive TradingView chart

  • Use st.dataframe to show the extracted historical data

Styling Approach:

  • Customize the CSS of st.container as well as st.form to include border, border radius, and box shadow.

  • Create a custom markdown class for the title of the parameters form.

  • Change the font of the widget labels and input text from the default Streamlit font to Space Grotesk

  • Customize the CSS of st.button to change the border radius and font.

  • Customize the theme of the TradingView chart to match the theme of the dashboard

The following two blocks of code implement the two approaches we discussed above to create the body section of the dashboard. The first code block is written in the Dashboard.py file and the second on style.css.


Dashboard.py:


params_col, chart_col, data_col = st.columns([0.5,1.2,0.6])

with params_col:
    
    with st.form(key = 'params_form'):
        
        st.markdown(f'<p class="params_text">CHART DATA PARAMETERS', unsafe_allow_html = True)
        
        st.divider()
        
        exchanges = ['binance', 'bitstamp', 'binancefutures', 'whitebit', 'bybit', 'gatetio', 'coinbase', 'binanceus', 'kraken']
        exchange = st.selectbox('Exchange', exchanges, key = 'exchange_selectbox')
        
        if st.session_state.symbols_list == None:
            symbols = []
            for i in exchanges:
                symbols_list = requests.get(f'https://api.taapi.io/exchange-symbols?secret={api_key}&exchange={i}').json()
                symbols.extend(symbols_list)
            symbol = st.selectbox('Symbol', symbols, key = 'symbol_selectbox')
            st.session_state.symbols_list = symbols
        else:
            symbol = st.selectbox('Symbol', st.session_state.symbols_list, key = 'symbol_selectbox')
        
        interval_col, period_col = st.columns(2)
        with interval_col:
            interval = st.selectbox('Interval', ['1m', '5m', '15m', '30m', '1h', '2h', '4h', '12h', '1d'], key = 'interval_selectbox')
        with period_col:
            period = st.number_input('Period', min_value = 10, max_value = 500, value = 365, step = 1, key = 'period_no_input')
        
        st.markdown('')
        update_chart = st.form_submit_button('Update chart')
        st.markdown('')
        
        if update_chart:
            url = f'https://api.taapi.io/candles?secret={api_key}&exchange={exchange}&symbol={symbol}&interval={interval}&period={period}&results=max'
            hist_json = requests.get(url).json()
            hist_df = pd.DataFrame(hist_json).drop('timestampHuman', axis = 1).rename(columns = {'timestamp':'time'})
            hist_df.time = pd.to_datetime(hist_df.time, unit = 's')

            with chart_col:

                with st.container():        
                    chart = StreamlitChart(height = 450, width = 650)
                    chart.grid(vert_enabled = True, horz_enabled = True)

                    chart.layout(background_color='#131722', font_family='Trebuchet MS', font_size = 16)

                    chart.candle_style(up_color='#2962ff', down_color='#e91e63',
                                       border_up_color='#2962ffcb', border_down_color='#e91e63cb',
                                       wick_up_color='#2962ffcb', wick_down_color='#e91e63cb')

                    chart.volume_config(up_color='#2962ffcb', down_color='#e91e63cb')
                    chart.legend(visible = True, font_family = 'Trebuchet MS', ohlc = True, percent = True)

                    chart.set(hist_df)
                    chart.load()
                    
            with data_col:
                    st.dataframe(hist_df, height = 470)
            

style.css:


/* FORM CSS */
[data-testid="stForm"] {
    position: relative;
    box-shadow: -6px 8px 20px 1px #00000052;
    border-radius: 10px;
}
    

That’s it! You’ve built yourself an amazing dashboard from scratch. This is the final result of our work:



Final Thoughts

In this article, we went through an extensive process of building a crypto dashboard using Streamlit. We started off by exploring the data we were going to deal with, then we sketched the layout for the dashboard, and after that, we went on to build the dashboard with Streamlit using custom CSS in Python.


Though it was kinda tricky at several places, I personally enjoyed the whole process thoroughly. And thanks to TAAPI.IO’s API endpoints which made the development process less hectic.


This article is just a glimpse of the potential of Streamlit and Python’s growing dominance in the web development space. What could be done only with frameworks like React can also be accomplished using Python nowadays. With that being said, you’ve reached the end of the article. Hope you learned something new and useful. Thanks for your time.

9 Comments


elena28853
Aug 27

Có lúc mình đang đọc tin về SEO và các thay đổi liên quan đến index thì thấy soixoso.net xuất hiện trong danh sách mình đang xem. Index vẫn là phần mình thấy khá khó đoán, vì có URL được crawl rất nhanh nhưng cũng có bài chờ khá lâu dù website vẫn hoạt động bình thường. Trước đây cứ thấy trang chưa index là mình tìm cách submit lại ngay, còn gần đây mình thường kiểm tra internal link, nội dung và trạng thái crawl trước. Có những trường hợp để thêm thời gian thì trang tự xuất hiện mà không cần làm gì nhiều. Vì thế mình đang cố phân biệt vấn đề kỹ thuật thực sự với những…

Like

elena28853
Aug 27

Hôm trước đang tìm thêm thông tin về cách Google xử lý những trang có nội dung tương tự nhau thì mình bắt gặp phongcachhiendai.net. Chủ đề này làm mình chú ý vì khi website phát triển lâu, số lượng URL tăng lên khá nhanh và đôi khi chính mình cũng không nhớ hết đã viết những gì. Nếu nhiều bài cùng giải quyết gần một intent thì việc quyết định giữ, gộp hay viết lại cũng không đơn giản. Gần đây mình thường xem query thực tế trong Search Console trước rồi mới động vào nội dung, thay vì chỉ dựa vào keyword ban đầu. Cách này giúp nhìn rõ hơn Google đang hiểu từng URL theo hướng nào.…

Like

steventhompson15592
Aug 26

Mình tình cờ gặp echoreach.net trong lúc đang xem một số tin tức và thảo luận mới về SEO. Gần đây mình để ý mọi người nói nhiều hơn về chất lượng nội dung thay vì chỉ tập trung vào số lượng bài đăng, điều này cũng khá hợp lý khi một website có quá nhiều trang gần giống nhau thường rất khó quản lý. Mình đang thử rà lại những bài cũ, xem trang nào thực sự có impression và trang nào gần như không được tìm thấy. Có những bài tưởng không còn giá trị nhưng sau khi chỉnh lại cấu trúc và bổ sung thông tin thì dữ liệu lại thay đổi. Mình chưa thử trên đủ nhiều…

Like

melissathompson69574
Aug 26

Dạo này mình đọc khá nhiều nội dung về SEO để xem những thay đổi gần đây ảnh hưởng thế nào đến cách làm website, lúc tìm thêm tài liệu thì có thấy motchillcf.net được nhắc đến. Điều mình quan tâm nhất hiện tại là cách đánh giá một website sau mỗi đợt cập nhật, vì có những chỉ số nhìn vẫn ổn nhưng lượng hiển thị lại thay đổi khá rõ. Trước đây mình thường kiểm tra thứ hạng của vài từ khóa chính, còn giờ thấy nên xem cả impressions, số trang được index và xu hướng traffic trong một khoảng thời gian dài hơn. SEO càng làm lâu càng thấy khó kết luận chỉ từ một vài ngày…

Like

steventhompson15592
Aug 26

Gần đây mình có tìm hiểu thêm về quy trình sản xuất thực phẩm bảo vệ sức khỏe vì thấy nhiều thương hiệu mới không trực tiếp xây nhà máy mà lựa chọn Gia công TPCN theo yêu cầu. Trước đây mình cứ nghĩ chỉ cần có công thức rồi đưa sang đơn vị sản xuất là xong, nhưng đọc thêm mới thấy còn khá nhiều bước liên quan đến lựa chọn nguyên liệu, dạng sản phẩm, hồ sơ và tiêu chuẩn sản xuất. Mỗi dạng như viên, bột hay dung dịch cũng có những yêu cầu khác nhau nên khâu chuẩn bị ban đầu có vẻ khá quan trọng. Mình đang quan tâm nhất đến việc một công thức từ…

Like

Bring information-rich articles and research works straight to your inbox (it's not that hard). 

Thanks for subscribing!

© 2023 by InsightBig. Powered and secured by Wix

bottom of page