Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Python for Finance Cookbook – Second Edition

You're reading from   Python for Finance Cookbook – Second Edition Over 80 powerful recipes for effective financial data analysis

Arrow left icon
Product type Paperback
Published in Dec 2022
Publisher Packt
ISBN-13 9781803243191
Length 740 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Eryk Lewinson Eryk Lewinson
Author Profile Icon Eryk Lewinson
Eryk Lewinson
Arrow right icon
View More author details
Toc

Table of Contents (18) Chapters Close

Preface 1. Acquiring Financial Data 2. Data Preprocessing FREE CHAPTER 3. Visualizing Financial Time Series 4. Exploring Financial Time Series Data 5. Technical Analysis and Building Interactive Dashboards 6. Time Series Analysis and Forecasting 7. Machine Learning-Based Approaches to Time Series Forecasting 8. Multi-Factor Models 9. Modeling Volatility with GARCH Class Models 10. Monte Carlo Simulations in Finance 11. Asset Allocation 12. Backtesting Trading Strategies 13. Applied Machine Learning: Identifying Credit Default 14. Advanced Concepts for Machine Learning Projects 15. Deep Learning in Finance 16. Other Books You May Enjoy
17. Index

Building an interactive web app for technical analysis using Streamlit

In this chapter, we have already covered the basics of technical analysis, which can help traders make their decision. However, until now everything was quite static—we downloaded the data, calculated an indicator, plotted it, and if we wanted to change the asset or the range of dates, we had to repeat all the steps. What if there was a better and more interactive way to approach this challenge?

This is exactly where Streamlit comes into play. Streamlit is an open source framework (and a company under the same name, similarly to Plotly) that allows us to build interactive web apps using only Python, all within minutes. Below you can find the highlights of Streamlit:

  • It is easy to learn and can generate results very quickly
  • It is Python only; no frontend experience is required
  • It allows us to focus purely on the data/ML sides of the app
  • We can use Streamlit’s hosting services for our apps

In this recipe, we will build an interactive app used for technical analysis. You will be able to select any of the constituents of the S&P 500 and carry out a simple analysis quickly and in an interactive way. What is more, you can easily expand the app to add more features such as different indicators and assets, or even embed backtesting of trading strategies within the app.

Getting ready

This recipe is slightly different than the rest. The code of our app “lives” in a single Python script (technical_analysis_app.py), which has around a hundred lines of code. A very basic app can be much more concise, but we wanted to go over some of the most interesting features of Streamlit, even if they are not strictly necessary to make a basic app for technical analysis.

In general, Streamlit executes code from top to bottom, which makes the explanation easier to fit into the structure used in this book. Thus, the steps in this recipe are not steps per se—they cannot/should not be executed on their own. Instead, they are a step-by-step walkthrough of all the components of the app. While building your own apps or expanding this one, you can freely change the order of the steps as you see fit (as long as they are aligned with Streamlit’s framework).

How to do it…

The following steps are all located in the technical_analysis_app.py:

  1. Import the libraries:
    import yfinance as yf
    import streamlit as st
    import datetime
    import pandas as pd
    import cufflinks as cf
    from plotly.offline import iplot
    cf.go_offline()
    
  2. Define a function for downloading a list of S&P 500 constituents from Wikipedia:
    @st.cache
    def get_sp500_components():
        df = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
        df = df[0]
        tickers = df["Symbol"].to_list()
        tickers_companies_dict = dict(
            zip(df["Symbol"], df["Security"])
        )
        return tickers, tickers_companies_dict
    
  3. Define a function for downloading historical stock prices using yfinance:
    @st.cache
    def load_data(symbol, start, end):
        return yf.download(symbol, start, end)
    
  4. Define a function for storing downloaded data as a CSV file:
    @st.cache
    def convert_df_to_csv(df):
        return df.to_csv().encode("utf-8")
    
  5. Define the part of the sidebar used for selecting the ticker and the dates:
    st.sidebar.header("Stock Parameters")
    available_tickers, tickers_companies_dict = get_sp500_components()
    ticker = st.sidebar.selectbox(
        "Ticker", 
        available_tickers, 
        format_func=tickers_companies_dict.get
    )
    start_date = st.sidebar.date_input(
        "Start date", 
        datetime.date(2019, 1, 1)
    )
    end_date = st.sidebar.date_input(
        "End date", 
        datetime.date.today()
    )
    if start_date > end_date:
        st.sidebar.error("The end date must fall after the start date")
    
  6. Define the part of the sidebar used for tuning the details of the technical analysis:
    st.sidebar.header("Technical Analysis Parameters")
    volume_flag = st.sidebar.checkbox(label="Add volume")
    
  7. Add the expander with parameters of the SMA:
    exp_sma = st.sidebar.expander("SMA")
    sma_flag = exp_sma.checkbox(label="Add SMA")
    sma_periods= exp_sma.number_input(
        label="SMA Periods", 
        min_value=1, 
        max_value=50, 
        value=20, 
        step=1
    )
    
  8. Add the expander with parameters of the Bollinger bands:
    exp_bb = st.sidebar.expander("Bollinger Bands")
    bb_flag = exp_bb.checkbox(label="Add Bollinger Bands")
    bb_periods= exp_bb.number_input(label="BB Periods", 
                                    min_value=1, max_value=50, 
                                    value=20, step=1)
    bb_std= exp_bb.number_input(label="# of standard deviations", 
                                min_value=1, max_value=4, 
                                value=2, step=1)
    
  9. Add the expander with parameters of the RSI:
    exp_rsi = st.sidebar.expander("Relative Strength Index")
    rsi_flag = exp_rsi.checkbox(label="Add RSI")
    rsi_periods= exp_rsi.number_input(
        label="RSI Periods", 
        min_value=1, 
        max_value=50, 
        value=20, 
        step=1
    )
    rsi_upper= exp_rsi.number_input(label="RSI Upper", 
                                    min_value=50, 
                                    max_value=90, value=70, 
                                    step=1)
    rsi_lower= exp_rsi.number_input(label="RSI Lower", 
                                    min_value=10, 
                                    max_value=50, value=30, 
                                    step=1)
    
  10. Specify the title and additional text in the app’s main body:
    st.title("A simple web app for technical analysis")
    st.write("""
        ### User manual
        * you can select any company from the S&P 500 constituents
    """)
    
  11. Load the historical stock prices:
    df = load_data(ticker, start_date, end_date)
    
  12. Add the expander with a preview of the downloaded data:
    data_exp = st.expander("Preview data")
    available_cols = df.columns.tolist()
    columns_to_show = data_exp.multiselect(
        "Columns", 
        available_cols, 
        default=available_cols
    )
    data_exp.dataframe(df[columns_to_show])
     
    csv_file = convert_df_to_csv(df[columns_to_show])
    data_exp.download_button(
        label="Download selected as CSV",
        data=csv_file,
        file_name=f"{ticker}_stock_prices.csv",
        mime="text/csv",
    )
    
  13. Create the candlestick chart with the selected TA indicators:
    title_str = f"{tickers_companies_dict[ticker]}'s stock price"
    qf = cf.QuantFig(df, title=title_str)
    if volume_flag:
        qf.add_volume()
    if sma_flag:
        qf.add_sma(periods=sma_periods)
    if bb_flag:
        qf.add_bollinger_bands(periods=bb_periods,
                               boll_std=bb_std)
    if rsi_flag:
        qf.add_rsi(periods=rsi_periods,
                   rsi_upper=rsi_upper,
                   rsi_lower=rsi_lower,
                   showbands=True)
    fig = qf.iplot(asFigure=True)
    st.plotly_chart(fig)
    

    To run the app, open the terminal, navigate to the directory in which the technical_analysis_app.py script is located, and run the following command:

    streamlit run technical_analysis_app.py
    

    Running the code opens the Streamlit app in your default browser. The app’s default screen looks as follows:

Figure 5.12: Our technical analysis app in the browser

The app is fully responsive to the inputs—anytime you change the inputs in the sidebar or the app’s main body, the displayed contents will adjust accordingly. Potentially, we could even take it a step further and connect our app to a broker via the broker’s API. This way, we could analyze the patterns in the app and create orders based on the outcome of our analyses.

How it works…

As mentioned in the Getting ready section, this recipe is structured differently. The steps are in fact a sequence of elements that all define the app we built. Before diving into the details, the general structure of the app’s codebase is as follows:

  • Imports and setup (step 1)
  • Data loading functions (steps 2–4)
  • Sidebar (steps 5–9)
  • App’s main body (steps 10–13)

In the first step, we imported the required libraries. For the technical analysis part, we decided to use a library that can visualize a selection of technical indicators in as few lines of code as possible. That is why we decided to go with cufflinks, which was introduced in Chapter 3, Visualizing Financial Time Series. However, in case you need to calculate a wider range of indicators, you can use any other library and create the plots yourself.

In Step 2, we defined a function for loading a list of S&P 500 constituents from Wikipedia. We used the pd.read_html to download the information from the table straight into a DataFrame. The function returns two elements: a list of valid tickers and a dictionary containing pairs of tickers and their corresponding companies’ names.

You surely have noticed that we used a @st.cache decorator while defining the function. We will not go over a lot of details of decorators in general, but we will cover what this one does as it is very handy while building an app using Streamlit. The decorator indicates that the app should cache the previously fetched data for later use. So in case we refresh the page or the function is called again, the data will not be downloaded/processed again (unless some conditions occur). This way, we can greatly increase the web app’s responsiveness and lower the end user’s waiting time.

Behind the scenes, Streamlit keeps track of the following information to determine if the data should be fetched again:

  • The input parameters that we provided while calling the function
  • The values of any external variables used in the function
  • The body of the called function
  • The body of any function called inside of the cached function

In short, if this is the first time Streamlit sees a certain combination of those four elements, it will execute the function and store its output in a local cache. If it encounters the very same set of items the next time the function is called, it will skip executing it and return the cached output from the previous execution.

Steps 3 and 4 contain very small functions. The first one is used to fetch the historical stock prices from Yahoo Finance using the yfinance library. The following step saves the output of a DataFrame into a CSV file, which is then encoded in UTF-8.

In Step 5, we started working on the app’s sidebar, which we use for storing the parameter configurations for the app. The first thing to notice is that all the elements that are meant to be located in the sidebar are called with st.sidebar (as opposed to just st, which we use when defining the main body’s elements and other functions). In this step, we did the following:

  • We specified the header.
  • We downloaded the list of available tickers.
  • We created a drop-down selection box of the available tickers. We also provided additional formatting by passing the dictionary containing symbol-name pairs to the format_func argument.
  • We allowed the users to select the start and end dates for the analysis. Using date_input displays an interactive calendar from which the users can select a date.
  • We accounted for invalid combinations of dates (start later than the end) by using an if statement together with st.sidebar.error. This will halt the app execution until the error is resolved, that is, until a proper input is provided.

The outcome of this step looks as follows:

Figure 5.13: Part of the sidebar where we can choose the ticket and start/end dates

In Step 6, we added another header to our sidebar and created a checkbox using st.checkbox. If checked, the assigned variable will hold a True value, False if unchecked.

In Step 7, we started with configuring the technical indicators. To keep the app clean, we used expanders (st.expander). Expanders are collapsible boxes, which we trigger to expand by pressing the plus icon. Inside, we stored two elements:

  • A checkbox indicating whether we want to display the SMA.
  • A numeric field in which we can specify the number of periods for the moving average. For that element, we used Streamlit’s number_input object. We provided the label, minimum/maximum values, the default value, and the step size (we can incrementally increase/decrease the value of the field by that number when we press the corresponding buttons).

When using expanders, we first instantiated one in the sidebar using exp_sma = st.sidebar.expander("SMA"). Then, when we wanted to add elements to the expander, for example, the checkbox, we used the following syntax: sma_flag = exp_sma.checkbox(label="Add SMA"). This way, it was added straight into the expander, not just the sidebar.

Steps 8 and 9 are very similar. We created two expanders for other technical indicators we wanted to include in the app – Bollinger bands and the RSI.

The code from Steps 7–9 generates the following part of the app’s sidebar:

Figure 5.14: Part of the sidebar where we can modify the parameters of the selected indicators

Then, we proceeded to define the app’s main body. In Step 10, we added the app’s title using st.title and added a user manual using st.write. When using the latter function, we can provide text input in a Markdown-formatted string. For this part, we used a subheader (indicated by ###) and created a list of bullets indicated by *. For brevity, we did not include all the text in the book, but you can find it in the book’s GitHub repository.

In Step 11, we downloaded the historical stock prices based on the inputs from the sidebar. What we could also have done here is download a full range of dates available for a given stock and only then use the sidebar’s start/end dates to filter out the periods of interest. By doing so, we would not have to redownload the data anytime we changed the start/end dates.

In Step 12, we defined another expander, this time in the app’s main body. First, we added a multiple selection field (st.multiselect) from which we can select any of the available columns from the downloaded historical prices. Then, we displayed the selected columns of the DataFrame for further inspection using st.dataframe. Lastly, we added the functionality to download the selected data (including the column selection) as a CSV file. For that, we used our convert_df_to_csv function, together with st.download_button.

Step 12 is responsible for generating the following part of the app:

Figure 5.15: Part of the app where we can inspect the DataFrame containing prices and download it as a CSV

In the app’s last step, we defined the figure we wanted to display. Without any of the technical analysis inputs, the app will display a candlestick chart using cufflinks. We instantiated the QuantFig object and then added elements to it depending on the inputs from the sidebar. Each of the Boolean flags triggers a separate command that adds an element to the plot. To display the interactive figure, we used st.plotly_chart, which works with plotly figures (cufflinks is a wrapper on top of plotly).

For other visualization libraries, there are different commands to embed visualizations. For example, for matplotlib, we would use st.pyplot. We could also display plots created in Altair using st.altair_chart.

There’s more…

In the first edition of the book, we covered a bit of a different approach to creating an interactive dashboard for technical analysis. Instead of Streamlit, we used ipywidgets to build the dashboard inside of a Jupyter notebook.

In general, Streamlit might be the better tool for this particular job, especially if we want to deploy the app (covered in the next recipe) and share it with others. However, ipywidgets can still be useful for other projects, which can live locally inside of a notebook. That is why you can find the code used for creating a very similar dashboard (within a notebook) in the accompanying GitHub repository.

See also

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image