top of page

Creating A Technical Stock Screener with Streamlit and Python

Nikhil Adithyan

A guide to integrating technical analysis into stock screening



Written in collaboration with Filippos Tzimopoulos


A stock screener is a vital tool for investors trying to find opportunities in the stock market. Identifying those opportunities can be overwhelming with thousands of publicly traded companies. Stock screeners streamline this process by filtering stocks based on criteria like market capitalization, price-to-earnings ratio, or dividend yield. This saves time, promotes data-driven decisions, and helps investors uncover opportunities, diversify portfolios, and align investments with their strategies.


Including technical analysis in stock, screeners enhance fundamental analysis by offering insights into price trends and market behavior. Indicators like moving averages or RSI help identify entry and exit points, validate trades, and uncover short-term opportunities, improving precision in dynamic markets


Creating customized stock screeners poses challenges, particularly with technical analysis data. Computing indicators across multiple stocks and timeframes requires extensive data processing, which can be resource-intensive. Services like TAAPI.IO address this by providing over 200 real-time technical indicators via a simple API. It supports bulk queries and custom datasets, reducing computational load and enabling developers to focus on building efficient screeners.


Another challenge is designing intuitive interfaces for filtering data. With numerous variables, users can feel overwhelmed. Streamlit, a Python-based framework, solves this by enabling dynamic user interfaces with minimal code.


What you should expect in this article


  • We are going to get live technical indicator values for more than 500 stocks using taapi.io’s stock technical indicator endpoints.

  • We will prepare these values into categories for better filtering

  • With Streamlit, we will create an interactive dashboard to present our screener

  • Finally, we will demonstrate some filters and how those can be used


Let’s get the data!

Initially, we will do the necessary imports and use taapi.io’s API to get all the supported symbols



import requests
import pandas as pd
from tqdm import tqdm

api_token = '<YOU API TOKEN>'

url = 'https://api.taapi.io/exchange-symbols'
querystring = {"secret":api_token, "type":"stocks"}
list_of_symbols = requests.get(url, params=querystring).json()

This way, we will have all the supported symbols in a list to iterate through them and get all the technical indicators we decide on.

Before moving further, make sure to opt for the Pro or Expert Plan which allows you to access all stock-related API endpoints.


For this article, we will use the below five technical analysis metrics:


  1. RSI (Relative Strength Index): A momentum oscillator measuring price changes to identify overbought or oversold conditions on a 0–100 scale

  2. SuperTrend: A trend-following indicator using price and ATR to determine trend direction and signal reversals

  3. Stochastic Oscillator: A momentum indicator comparing closing prices to their range over a set period to find overbought/oversold levels

  4. VWAP (Volume-Weighted Average Price): A benchmark showing the average price of an asset, weighted by trading volume, over a specific period

  5. Doji Star Pattern: A candlestick formation indicating market indecision, formed when opening and closing prices are nearly equal


Note that Taapi.io offers technical indicators and pattern formations like the Doji Star.


To get those indicators, even though we can still get them one by one, we will use a method called “construct,” which is very useful with one call to get various indicators at once! For that reason, it is essential to understand the payload to be used:



payload = {
 "secret": api_token,
 "construct": {
  "type": "stocks",
  "symbol": "AAPL",
  "interval": "1d",
  "indicators": [
   {
    "indicator": "price"
   },
   {
    "indicator": "rsi",
    "period": 14
   },
   {
    "indicator": "supertrend"
   },
            {
    "indicator": "stoch"
   },
            {
    "indicator": "vwap"
   },
            {
    "indicator": "dojistar", "results":20, "addResultTimestamp":True
   }
  ]
 }
}

In this payload, you can notice the following:


  • The stock is AAPL, but we will change it during the interaction later to get the data

  • We will request the 1 day timeframe

  • We will request a list of the five indicators

  • As an example, we will set the period for RSI, so we see how you can pass other parameters for each indicator

  • For the Doji Star, we will request the last 20 values, so we will calculate in the screener the last time that a positive or negative pattern was formed.


Now, with the below loop, we are going to get the data for each stock in our list:



data_list = []

for symbol in tqdm(list_of_symbols):

    row = {"symbol":symbol}

    url = 'https://api.taapi.io/bulk'
    payload['construct']['symbol'] = symbol
    response = requests.post(url, json=payload)

    if response.status_code == 200:
        for indicator in response.json()['data']:
            if indicator['indicator'] == 'price':
                row['PRICE'] = indicator['result']['value']
            elif indicator['indicator'] == 'rsi':
                row['RSI'] = indicator['result']['value']
            elif indicator['indicator'] == 'supertrend':
                row['SUPERTREND_VALUE'] = indicator['result']['value']
                row['SUPERTREND_ADVICE'] = indicator['result']['valueAdvice']
            elif indicator['indicator'] == 'stoch':
                row['STOCH_K'] = indicator['result']['valueK']
                row['STOCH_D'] = indicator['result']['valueD']
            elif indicator['indicator'] == 'vwap':
                row['VWAP'] = indicator['result']['value']
            elif indicator['indicator'] == 'dojistar':
                    df = pd.DataFrame(indicator['result']).sort_values(by='timestamp', ascending=False).reset_index()
                    last_pattern_timestamp_pos = df[df['value'] == 100]['timestamp'].max()
                    if pd.isna(last_pattern_timestamp_pos):
                        row['DOJISTAR_LAST_PATTERN_POSITIVE'] = 0
                    else:
                        row['DOJISTAR_LAST_PATTERN_POSITIVE'] = int(df[df['timestamp'] == last_pattern_timestamp_pos].index[0] + 1)
                    last_pattern_timestamp_neg = df[df['value'] == -100]['timestamp'].max()
                    if pd.isna(last_pattern_timestamp_neg):
                        row['DOJISTAR_LAST_PATTERN_NEGATIVE'] = 0
                    else:
                        row['DOJISTAR_LAST_PATTERN_NEGATIVE'] = int(df[df['timestamp'] == last_pattern_timestamp_neg].index[0] + 1)
    else:
        print(f'Gor {response.status_code} for {symbol} with error {response.json()}')

    # add data to the list
    data_list.append(row)

# convert the list of dictionaries to dataframe
df = pd.DataFrame(data_list)
df

This way, we gathered all the values in a dataframe. Now, we will convert some of them into categories to make our lives easier during the filtering. Specifically:


  • RSI will not be the value but Overbought, OverSold, Neutral

  • Stochastic will be low, medium, and high

  • VWAP will be above average, below, or average based on the mean of all the stocks in our screener.



def prepare_rsi(row):
    if row['RSI'] < 30:
        return 'oversold'
    elif row['RSI'] > 70:
        return 'overbought'
    else:
        return 'neutral'

def classify_stochastic(value):
    if value < 20:
        return 'low'
    elif value > 80:
        return 'high'
    else:
        return 'medium'

def classify_vwap(vwap):
    if vwap < 0.9 * vwap_mean:
        return 'below average'
    elif vwap > 1.1 * vwap_mean:
        return 'above average'
    else:
        return 'average'

df['RSI'] = df.apply(prepare_rsi, axis=1)

vwap_mean = df['VWAP'].mean()
df['VWAP'] = df['VWAP'].apply(classify_vwap)

df['STOCH_K'] = df['STOCH_K'].apply(classify_stochastic)
df['STOCH_D'] = df['STOCH_D'].apply(classify_stochastic)

data = df[['symbol', 'PRICE', 'RSI', 'SUPERTREND_ADVICE','VWAP','STOCH_K', 'STOCH_D',
       'DOJISTAR_LAST_PATTERN_POSITIVE','DOJISTAR_LAST_PATTERN_NEGATIVE',]]

data.to_csv('data.csv', index=False)

Finally, we will save the data into a CSV file using Streamlit.


The Screener!

As said before, we will now use streamlit to present our screener and be able to filter the data according to our needs.


To do that, we will create a Python file called app.py.



import pandas as pd
import streamlit as st
import numpy as np

# Create a DataFrame
stocks_data = pd.read_csv('<PATH TO data.csv>')
total_rows = stocks_data.shape[0]

st.set_page_config(page_title='Stock Technical Screener', page_icon=':chart_with_upwards_trend:', layout='wide')

st.sidebar.header('Options')

symbol_selected = st.sidebar.text_input('Symbol (Empty for no filter)', '')

rsi = st.sidebar.multiselect('RSI', options=stocks_data['RSI'].unique(), default=stocks_data['RSI'].unique())
supertrend = st.sidebar.multiselect('SUPERTREND', options=stocks_data['SUPERTREND_ADVICE'].unique(), default=stocks_data['SUPERTREND_ADVICE'].unique())
vwap = st.sidebar.multiselect('VWAP', options=stocks_data['VWAP'].unique(), default=stocks_data['VWAP'].unique())
stoch_k = st.sidebar.multiselect('STOCH_K', options=stocks_data['STOCH_K'].unique(), default=stocks_data['STOCH_K'].unique())
stoch_d = st.sidebar.multiselect('STOCH_D', options=stocks_data['STOCH_D'].unique(), default=stocks_data['STOCH_D'].unique())
doji_pos_min, doji_pos_max = st.sidebar.slider('DOJISTAR LAST POSITIVE', 0, 20, (0, 20), step=1)
doji_neg_min, doji_neg_max = st.sidebar.slider('DOJISTAR LAST NEGATIVE', 0, 20, (0, 20), step=1)

query = "RSI == @rsi & STOCH_K == @stoch_k & STOCH_D == @stoch_d & VWAP == @vwap & SUPERTREND_ADVICE == @supertrend & DOJISTAR_LAST_PATTERN_POSITIVE >= @doji_pos_min & DOJISTAR_LAST_PATTERN_POSITIVE <= @doji_pos_max & DOJISTAR_LAST_PATTERN_NEGATIVE >= @doji_neg_min & DOJISTAR_LAST_PATTERN_NEGATIVE <= @doji_neg_max"
if symbol_selected != '':
    query += f" & symbol == '{symbol_selected}'"
stocks_data_selection  = stocks_data.query(query)

stocks_data_selection_rows = stocks_data_selection.shape[0]

st.write (f'Displaying {stocks_data_selection_rows} from {total_rows} symbols ({(stocks_data_selection_rows/total_rows * 100):.2f}%)')
st.dataframe(stocks_data_selection, height=800, hide_index=True)

There are various tutorials over the web explaining the architecture of streamlit, so for this article, we will just explain what this file does without getting into overwhelming details for each line of code:


  • We will configure the page to be named “Stock Technical Screener” with a nice bag of money icon ;)

  • We will set up the sidebar with the necessary filters

  • The ticker text box should be left empty if we want to see all the stocks based on our filters. If we just want to see one stock, we can place it there

  • We are going to use the unique values for all the indicators as filter options except the Doji Star

  • For the Doji Star, we will use a slider so the user can request which values he wants to be filtered from 0 (no formation in the last 20 days) to 20 maximum

  • Ultimately, we will filter the data based on our selections, showing the dataframe and how many stocks are included with our filters from the total.


Now, by running the below command prompt, you should be able to see your screener in your default browser.



streamlit run <PATH TO YOUR LOCAL FOLDER>/app.py


Now you are ready to do your “homework”. Let’s look at a couple of examples and see which stocks were filtered when running this process.


Go Long!


To go long, a possible scenario can be the following:


  • The RSI should not be overbought

  • The SuperTrend should be on the long side

  • The VWAP should not be above average

  • We should have a positive Doji star pattern in the last 20 candles and no negative. So the slide bar for positive should be 1–20 and negative 0.



Our streamlit app will show us 16 stocks that match our criteria. Clicking on the positive Doji star header will sort those in ascending order.

Now, things become clearer. KDP (Keurig Dr Pepper Inc.) looks like it had the most recent positive Doji star formation, while it seems like VWAP (below average) and SuperTrend (long) agree that it is a good opportunity.


Additionally, since we did not add Stochastic in the filter, we can see that HRL (Hormel Foods Corporation) is a stock that also had a recent Doji Star pattern formed, and additionally, Stochastic is on the Low Side, which also gives as an additional opportunity.


Go Short!


Now, let’s see how a short strategy would show. We will filter as below:


  • RSI should not be Oversold

  • SuperTrend should be on the short side

  • VWAP should not be below average

  • We should have a recent formation of a negative Doji star and no positive one



This will give us 15 stocks identifying NXPI (NXP Semiconductors N.V.) and EQIX (Equinix, Inc.) that recently formed a negative Doji star pattern. This might be a signal to go short on those.


What about the fruit?

As described before, you can also request the data of one only stock, so let’s see Apple by inputting into the Symbol textbox the ticker AAPL (you should not have any other filters applied to be sure you will see it)



Looks like while RSI flags Apple as Overbought, SuperTrend has the opinion that we still should go long, while VWAP notices that the pricing is fair.


Conclusion

This article showcased building a stock screener combining technical analysis indicators using TAAPI.IO and an interactive Streamlit dashboard. Users can efficiently filter stocks and identify trading opportunities by integrating metrics like RSI, SuperTrend, and VWAP. TAAPI.IO’s extensive library of indicators simplifies data retrieval, allowing developers to focus on creating tailored solutions.


As always, there is scope for improving the screener with ideas like:


  • Add fundamental data like earnings or dividend yields for a broader analysis.

  • Enable user-defined indicator parameters for greater customization.

  • Automate daily data updates for up-to-date insights.


Stock screeners empower investors to navigate markets with precision. And this article is just a kick-start for your investing journey using informed decision-making. So start exploring, customizing, and making it your own — happy investing!


Disclaimer: While we explore the exciting world of investing in this article, it’s crucial to note that the information provided is for educational purposes only. I’m not a financial advisor, and the content here doesn’t constitute financial advice. Always do your research and consider consulting with a professional before making any investment decisions.

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