├── .gitignore ├── .streamlit └── config.toml ├── LICENSE.txt ├── README.md ├── candlestick_patterns.py ├── docs └── images │ ├── app_flow.png │ ├── ndx_chart.png │ ├── ndx_earnings.png │ ├── ndx_scan.png │ ├── sp500_chart.png │ └── sp500_earnings.png ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | *.DS_Store 3 | 4 | # Environments 5 | venv/ 6 | 7 | # Unit tests 8 | __pycache__/ 9 | 10 | # Miscellaneous 11 | *.pyc 12 | 13 | -------------------------------------------------------------------------------- /.streamlit/config.toml: -------------------------------------------------------------------------------- 1 | [theme] 2 | base="dark" 3 | primaryColor="cyan" -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 John Muchow - https://github.com/johnmuchow 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Candlestick Pattern Matching 2 | 3 | This project scans all the symbols in the Nasdaq 100 and S&P 500 for candlestick patterns. Symbols showing candlestick patterns are viewable as a [TradingView](http://TradingView.com) chart in a webapp. 4 | 5 | This project was created in Python and runs as a web application via the Python web-framework [Streamlit](https://Streamlit.io). 6 | 7 | 8 | ## Application Flow 9 | 10 | ### Download the Data 11 | 12 | * Download list of symbols in the Nasdaq 100 and S&P 500 from WikiPedia. 13 | * For each symbol, download 10 days of data (open, high, low, close) using Python package [yfinance](https://pypi.org/project/yfinance/) 14 | * For each symbol, download earnings data using Python package [yahooquery](https://yahooquery.dpguthrie.com). 15 | 16 | ### Scan Stock Symbols for Candlestick Patterns 17 | 18 | * For each of the 60+ candlestick patterns, scan all the symbols looking for pattern matches. 19 | * Create list of patterns and the relevant symbols. 20 | 21 | ### Streamlit Webapp 22 | 23 | With a list of candlestick patterns and symbols matching those patterns: 24 | 25 | * Create drop-down lists in Streamlit with name of candlestick pattern and the symbol. 26 | * Upon selection of a pattern/symbol from the drop-down, load a TradingView chart. 27 | * Create plot of earnings data using [Ploty Express](https://plotly.com/python/plotly-express/). 28 | 29 | ![Application flow](docs/images/app_flow.png) 30 | 31 | ### Screenshots 32 | 33 | ![Nasdaq 100 scan](docs/images/ndx_scan.png) 34 | 35 | ![Nasdaq 100 chart](docs/images/ndx_chart.png) 36 | 37 | ![Symbol earnings](docs/images/ndx_earnings.png) 38 | 39 | ![S&P 500 chart](docs/images/sp500_chart.png) 40 | 41 | ![Symbol earnings](docs/images/sp500_earnings.png) 42 | 43 | 44 | ## Candlestick Pattern Matching in Python Using TA-Lib 45 | 46 | [TA-Lib](https://ta-lib.org) is a Python wrapper based on [Cython](https://cython.org/) and includes the following: 47 | 48 | * 150+ indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands, etc. 49 | * 60+ candlestick pattern recognition 50 | * Open-source API for C/C++, Java, Perl, Python and 100% Managed .NET 51 | 52 | 53 | ## Install Python Requirements 54 | 55 | ```bash 56 | pip install -r requirements 57 | ``` 58 | 59 | ## Usage 60 | 61 | ```bash 62 | $ streamlit main.py 63 | ``` 64 | 65 | ## Ideas for Updates 66 | 67 | * Store all data in Snowflake or a similiar cloud-based solution. 68 | * Add additional support for Streamlit caching. 69 | * Add asynchronous downloading of data. 70 | 71 | ## TradingView Projects 72 | 73 | If you're a trader/investor, I've written many techical indicators, both open/closed source that might be of interest: 74 | [TradingView Indicators](https://www.tradingview.com/u/JohnMuchow/#published-scripts). 75 | 76 | ## License 77 | 78 | This project is licensed under the MIT License. See the LICENSE file for more details. 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /candlestick_patterns.py: -------------------------------------------------------------------------------- 1 | candlesticks = { 2 | 'CDL2CROWS':'Two Crows', 3 | 'CDL3BLACKCROWS':'Three Black Crows', 4 | 'CDL3INSIDE':'Three Inside Up/Down', 5 | 'CDL3LINESTRIKE':'Three-Line Strike', 6 | 'CDL3OUTSIDE':'Three Outside Up/Down', 7 | 'CDL3STARSINSOUTH':'Three Stars In The South', 8 | 'CDL3WHITESOLDIERS':'Three Advancing White Soldiers', 9 | 'CDLABANDONEDBABY':'Abandoned Baby', 10 | 'CDLADVANCEBLOCK':'Advance Block', 11 | 'CDLBELTHOLD':'Belt-hold', 12 | 'CDLBREAKAWAY':'Breakaway', 13 | 'CDLCLOSINGMARUBOZU':'Closing Marubozu', 14 | 'CDLCONCEALBABYSWALL':'Concealing Baby Swallow', 15 | 'CDLCOUNTERATTACK':'Counterattack', 16 | 'CDLDARKCLOUDCOVER':'Dark Cloud Cover', 17 | 'CDLDOJI':'Doji', 18 | 'CDLDOJISTAR':'Doji Star', 19 | 'CDLDRAGONFLYDOJI':'Dragonfly Doji', 20 | 'CDLENGULFING':'Engulfing Pattern', 21 | 'CDLEVENINGDOJISTAR':'Evening Doji Star', 22 | 'CDLEVENINGSTAR':'Evening Star', 23 | 'CDLGAPSIDESIDEWHITE':'Up/Down-gap side-by-side white lines', 24 | 'CDLGRAVESTONEDOJI':'Gravestone Doji', 25 | 'CDLHAMMER':'Hammer', 26 | 'CDLHANGINGMAN':'Hanging Man', 27 | 'CDLHARAMI':'Harami Pattern', 28 | 'CDLHARAMICROSS':'Harami Cross Pattern', 29 | 'CDLHIGHWAVE':'High-Wave Candle', 30 | 'CDLHIKKAKE':'Hikkake Pattern', 31 | 'CDLHIKKAKEMOD':'Modified Hikkake Pattern', 32 | 'CDLHOMINGPIGEON':'Homing Pigeon', 33 | 'CDLIDENTICAL3CROWS':'Identical Three Crows', 34 | 'CDLINNECK':'In-Neck Pattern', 35 | 'CDLINVERTEDHAMMER':'Inverted Hammer', 36 | 'CDLKICKING':'Kicking', 37 | 'CDLKICKINGBYLENGTH':'Kicking - bull/bear determined by the longer marubozu', 38 | 'CDLLADDERBOTTOM':'Ladder Bottom', 39 | 'CDLLONGLEGGEDDOJI':'Long Legged Doji', 40 | 'CDLLONGLINE':'Long Line Candle', 41 | 'CDLMARUBOZU':'Marubozu', 42 | 'CDLMATCHINGLOW':'Matching Low', 43 | 'CDLMATHOLD':'Mat Hold', 44 | 'CDLMORNINGDOJISTAR':'Morning Doji Star', 45 | 'CDLMORNINGSTAR':'Morning Star', 46 | 'CDLONNECK':'On-Neck Pattern', 47 | 'CDLPIERCING':'Piercing Pattern', 48 | 'CDLRICKSHAWMAN':'Rickshaw Man', 49 | 'CDLRISEFALL3METHODS':'Rising/Falling Three Methods', 50 | 'CDLSEPARATINGLINES':'Separating Lines', 51 | 'CDLSHOOTINGSTAR':'Shooting Star', 52 | 'CDLSHORTLINE':'Short Line Candle', 53 | 'CDLSPINNINGTOP':'Spinning Top', 54 | 'CDLSTALLEDPATTERN':'Stalled Pattern', 55 | 'CDLSTICKSANDWICH':'Stick Sandwich', 56 | 'CDLTAKURI':'Takuri (Dragonfly Doji with very long lower shadow)', 57 | 'CDLTASUKIGAP':'Tasuki Gap', 58 | 'CDLTHRUSTING':'Thrusting Pattern', 59 | 'CDLTRISTAR':'Tristar Pattern', 60 | 'CDLUNIQUE3RIVER':'Unique 3 River', 61 | 'CDLUPSIDEGAP2CROWS':'Upside Gap Two Crows', 62 | 'CDLXSIDEGAP3METHODS':'Upside/Downside Gap Three Methods' 63 | } -------------------------------------------------------------------------------- /docs/images/app_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/app_flow.png -------------------------------------------------------------------------------- /docs/images/ndx_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/ndx_chart.png -------------------------------------------------------------------------------- /docs/images/ndx_earnings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/ndx_earnings.png -------------------------------------------------------------------------------- /docs/images/ndx_scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/ndx_scan.png -------------------------------------------------------------------------------- /docs/images/sp500_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/sp500_chart.png -------------------------------------------------------------------------------- /docs/images/sp500_earnings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmuchow/Python-Candlestick-Pattern-Matching/ce8cf1e22aeed9fa0b8fda1b57579fd25d40508c/docs/images/sp500_earnings.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import os 3 | import array 4 | import time 5 | import pandas as pd 6 | import yfinance as yf 7 | import yahooquery as yq 8 | import talib 9 | import plotly.express as px 10 | import plotly.graph_objs as go 11 | from datetime import datetime, timedelta 12 | from candlestick_patterns import candlesticks 13 | from yahooquery import Ticker 14 | 15 | # Array of symbols 16 | ndx_list = [] 17 | sp500_list = [] 18 | 19 | #------------------------------------------------------------------------------- 20 | # Paths, files and urls 21 | #------------------------------------------------------------------------------- 22 | ndx_url = 'https://en.wikipedia.org/wiki/Nasdaq-100' 23 | sp500_url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies' 24 | 25 | ndx_data_directory = 'data/ndx' 26 | ndx_symbols = ndx_data_directory + '/ndx.txt' 27 | ndx_test_symbol = ndx_data_directory + '/aapl.csv' 28 | 29 | sp500_data_directory = 'data/sp500' 30 | sp500_symbols = sp500_data_directory + '/sp500.txt' 31 | sp500_test_symbol = sp500_data_directory + '/tsla.csv' 32 | 33 | #------------------------------------------------------------------------------- 34 | # Download symbols from wikipedia and write to local file 35 | #------------------------------------------------------------------------------- 36 | def build_symbol_list(url, table_num, column_name, output_file): 37 | 38 | list = [] 39 | 40 | # Download and sort list of symbols 41 | list = sorted(pd.read_html(url)[table_num][column_name].tolist()) 42 | 43 | # Save list to file 44 | with open(output_file, 'w') as file: 45 | for item in list: 46 | file.write(str(item) + '\n') 47 | 48 | return list 49 | 50 | #------------------------------------------------------------------------------- 51 | # For each symbol in the list, download data into csv 52 | #------------------------------------------------------------------------------- 53 | @st.cache_data 54 | def download_symbol_data(test_file, symbols_file, output_dir, message): 55 | 56 | # If there is already a csv file, assume we've already created all the csv files 57 | if (not os.path.exists(test_file)): 58 | 59 | with open(symbols_file, 'r') as f: 60 | symbol_list = f.read().split() 61 | 62 | start_date = datetime.today() 63 | 64 | # Subtract 10 days, excluding weekends 65 | days_to_subtract = 10 66 | days_subtracted = 0 67 | while days_subtracted < days_to_subtract: 68 | start_date -= timedelta(days=1) 69 | if start_date.weekday() >= 5: # If it's a weekend day (5 = Saturday, 6 = Sunday) 70 | continue 71 | days_subtracted += 1 72 | 73 | # Progress bar 74 | progress_text = message 75 | pbar = st.sidebar.progress(0, text=progress_text) 76 | percent_complete = 0.0 77 | increment = 1 / len(symbol_list) 78 | 79 | for symbol in symbol_list: 80 | data = yf.download(symbol, start=start_date, end=datetime.today(), progress=False) 81 | data.to_csv('{}/{}.csv'.format(output_dir, symbol)) 82 | 83 | if (percent_complete <= 1): 84 | pbar.progress(percent_complete, text=progress_text) 85 | percent_complete += increment 86 | 87 | # Hide the progress bar 88 | pbar.empty() 89 | 90 | #------------------------------------------------------------------------------- 91 | # Get additional data via yahooquery and prep to plot 92 | #------------------------------------------------------------------------------- 93 | def getEarningsData(symbol): 94 | 95 | ticker = Ticker(symbol) 96 | df = ticker.earning_history 97 | 98 | est = ticker.earnings[symbol]['earningsChart']['currentQuarterEstimate'] 99 | 100 | fig = px.bar(df, x="period", 101 | y=['epsEstimate', 'epsActual'], 102 | title=f"{symbol.upper()} - Past Earning's Estimates and Actuals", 103 | barmode='group') 104 | fig.add_hline(y=est, line_color='yellow', line_width=2, line_dash='dash', annotation_text=est) 105 | st.plotly_chart(fig) 106 | 107 | 108 | header = dict(values=['Current Quarter Estimate']) 109 | cells = dict(values=[ticker.earnings[symbol]['earningsChart']['currentQuarterEstimate']]) 110 | table = go.Table(header=header, cells=cells) 111 | layout = go.Layout(height=500, width=300, font=dict(size=14)) 112 | figx = go.Figure(data=[table], layout = layout) 113 | st.plotly_chart(figx) 114 | 115 | #------------------------------------------------------------------------------- 116 | # TradingView related to embed charts 117 | #------------------------------------------------------------------------------- 118 | def show_tradingview_chart(symbol): 119 | 120 | # Set TradingView chart's HTML code 121 | tradingview_chart = f""" 122 | 123 |
124 |
125 | 126 | 142 |
143 | 144 | """ 145 | 146 | # Display TradingView chart using components.html 147 | st.components.v1.html(tradingview_chart, height=650) 148 | 149 | #------------------------------------------------------------------------------- 150 | # For the passed in symbol, does it match the candlestick pattern 151 | #------------------------------------------------------------------------------- 152 | def process_symbol(data_directory, symbol, pattern): 153 | 154 | talib_function = getattr(talib, pattern) 155 | 156 | fullpath = os.path.join(data_directory, symbol + '.csv') 157 | 158 | # Check if the file is a file (i.e., not a directory) 159 | if (os.path.isfile(fullpath)): 160 | 161 | # Get dataframe 162 | df = pd.read_csv(fullpath) 163 | 164 | # Ignore any errors in file such as 'NaN' or an empty file 165 | try: 166 | # Call talib candlestick function with the symbol df 167 | ret = talib_function(df['Open'], df['High'], df['Low'], df['Close']) 168 | 169 | # We only need the last value to know if the data symbol is showing the 170 | # candlestick pattern. Using tail where the '1' is how many to get from the end. 171 | # The result is an array, so we use [0] to get the first value 172 | last = ret.tail(1).values[0] 173 | 174 | # talib returns 100 for bullish, -100 for bearish, only concerned about bullish 175 | if (last == 100): 176 | return True 177 | else: 178 | return False 179 | 180 | except: 181 | pass 182 | 183 | #------------------------------------------------------------------------------- 184 | # For all the patterns, scan all the symbols 185 | # e.g. CDLENGULFING, scan all 100 nasdaq symbols or all s&p500 symbol 186 | # 187 | # The pattern_matching_list[] will look as follows: 188 | # ('CDL3OUTSIDE', 'WMT') 189 | # ('CDLENGULFING', 'ALLE') 190 | # ('CDLENGULFING', 'WBD') 191 | # ('CDLHIKKAKE', 'ARE') 192 | # 193 | # Before returning the above list, make it more readable list for drop-down 194 | # ('Three Outside Up/Down', 'WMT') 195 | # ('Engulfing Pattern', 'ALLE') 196 | # ('Engulfing Pattern', 'WBD') 197 | #------------------------------------------------------------------------------- 198 | @st.cache_data 199 | def scan_symbols_for_candlestick_patterns(data_directory, symbol_list, progress_text): 200 | 201 | # for each candlestick pattern, loop through each symbol 202 | # if the symbol shows the pattern, add pattern to list of successful matches, 203 | # bump counter of matches for the candlestick pattern 204 | 205 | # Used for the progress bar 206 | total_entries = len(candlesticks) 207 | 208 | pattern_matching_list = [] 209 | 210 | # Loop through the keys, which refer to the TALIB function 211 | # 'CDL2CROWS':'Two Crows', 212 | # 'CDL3BLACKCROWS':'Three Black Crows', 213 | 214 | # Progress bar 215 | pbar = st.sidebar.progress(0, text=progress_text) 216 | percent_complete = 0.0 217 | increment = 1 / total_entries 218 | 219 | # For all candlestick patterns 220 | for pattern in candlesticks.keys(): 221 | 222 | # For all symbols 223 | for symbol in symbol_list: 224 | 225 | # Does symbol meet the pattern 226 | if (process_symbol(data_directory, symbol, pattern)): 227 | pattern_matching_list.append((pattern, symbol)) 228 | 229 | if (percent_complete <= 1): 230 | pbar.progress(percent_complete, text=progress_text) 231 | percent_complete += increment 232 | 233 | # Hide the progress bar 234 | pbar.empty() 235 | 236 | # Make the pattern_matching_list[] more readable 237 | new_list = [] 238 | for i in pattern_matching_list: 239 | # Search dict of candlestick (CDLENGULFING, 'Engulfing') 240 | value = candlesticks.get(i[0]) 241 | new_list.append((value, i[1])) 242 | 243 | return new_list 244 | 245 | #------------------------------------------------------------------------------- 246 | # Main processing loop 247 | #------------------------------------------------------------------------------- 248 | def main_loop(ndx, sp500): 249 | 250 | ndx_result_list = [] 251 | sp500_result_list = [] 252 | 253 | #----------------------------------------------- 254 | # Streamlit sidebar - Download progress bars 255 | #----------------------------------------------- 256 | # Get list of ndx symbols, read file it exists, otherwise download from wikipedia 257 | # For nasdaq, we need the fifth table and column named 'Ticker' 258 | if (ndx): 259 | ndx_list = build_symbol_list(url=ndx_url, table_num = 4, column_name='Ticker', output_file=ndx_symbols) 260 | 261 | # Nasdaq 100 262 | # Download symbol data into csv files 263 | # Scan for candlestick patterns 264 | download_symbol_data(ndx_test_symbol, ndx_symbols, ndx_data_directory, 'Downloading Nasdaq 100 Data') 265 | ndx_result_list = scan_symbols_for_candlestick_patterns(ndx_data_directory, ndx_list, "Scanning Nasdaq 100 for Candlestick Patterns...") 266 | 267 | if (sp500): 268 | # For S&P 500, we need the first table (0) and column named 'Symbol' 269 | sp500_list = build_symbol_list(url=sp500_url, table_num = 0, column_name='Symbol', output_file=sp500_symbols) 270 | 271 | # Download symbol data into csv files 272 | # Scan for candlestick patterns 273 | download_symbol_data(sp500_test_symbol, sp500_symbols, sp500_data_directory, 'Downloading S&P 500 Data') 274 | sp500_result_list = scan_symbols_for_candlestick_patterns(sp500_data_directory, sp500_list, "Scanning S&P 500 for Candlestick Patterns...") 275 | 276 | #----------------------------------------------- 277 | # Streamlit main - tabs 278 | # Add css to change font size of tab text 279 | #----------------------------------------------- 280 | tab_titles = ['Nasdaq 100', 'S&P 500'] 281 | tabs = st.tabs(tab_titles) 282 | css = ''' 283 | 288 | ''' 289 | st.markdown(css, unsafe_allow_html=True) 290 | 291 | with tabs[0]: 292 | # Will return this format: ('Engulfing Pattern', 'ADI') 293 | selection_ndx = st.selectbox('Nasdaq 100', sorted(ndx_result_list), key='ndx', label_visibility='hidden') 294 | 295 | # use [1] as we want to pass in the symbol, 'ADI' from the above example 296 | if (selection_ndx != None): 297 | show_tradingview_chart(selection_ndx[1]) 298 | getEarningsData(selection_ndx[1]) 299 | 300 | with tabs[1]: 301 | # Will return this format: JM ... 302 | selection_sp500 = st.selectbox('S&P 500', sorted(sp500_result_list), key='sp500', label_visibility='hidden') 303 | 304 | if (selection_sp500 != None): 305 | show_tradingview_chart(selection_sp500[1]) 306 | getEarningsData(selection_sp500[1]) 307 | 308 | @st.cache_data 309 | #------------------------------------------------------------------------------- 310 | # Initial setup code run once 311 | #------------------------------------------------------------------------------- 312 | def initial_setup(): 313 | 314 | # Create data directories 315 | if not os.path.isdir(ndx_data_directory): 316 | os.makedirs(ndx_data_directory) 317 | 318 | if not os.path.isdir(sp500_data_directory): 319 | os.makedirs(sp500_data_directory) 320 | 321 | #------------------------------------------------------------------------------- 322 | # Main 323 | #------------------------------------------------------------------------------- 324 | def main(): 325 | 326 | st.set_page_config(page_title='Candlestick Patterns', page_icon=':chart_with_upwards_trend:', layout='wide') 327 | st.header('Candlestick Patterns') 328 | 329 | initial_setup() 330 | 331 | col1, col2 = st.sidebar.columns(2) 332 | ndx = col1.checkbox('Nasdaq 100') 333 | sp500 = col2.checkbox('S&P 500') 334 | 335 | main_loop(ndx, sp500) 336 | 337 | if __name__ == "__main__": 338 | main() 339 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altair==4.2.2 2 | appdirs==1.4.4 3 | attrs==22.2.0 4 | beautifulsoup4==4.12.2 5 | blinker==1.6.1 6 | cachetools==5.3.0 7 | certifi==2022.12.7 8 | cffi==1.15.1 9 | charset-normalizer==3.1.0 10 | click==8.1.3 11 | cryptography==40.0.1 12 | decorator==5.1.1 13 | entrypoints==0.4 14 | frozendict==2.3.7 15 | gitdb==4.0.10 16 | GitPython==3.1.31 17 | html5lib==1.1 18 | idna==3.4 19 | importlib-metadata==6.3.0 20 | Jinja2==3.1.2 21 | jsonschema==4.17.3 22 | lxml==4.9.2 23 | markdown-it-py==2.2.0 24 | MarkupSafe==2.1.2 25 | mdurl==0.1.2 26 | multitasking==0.0.11 27 | numpy==1.24.2 28 | packaging==23.0 29 | pandas==1.5.3 30 | patsy==0.5.3 31 | Pillow==9.5.0 32 | plotly==5.14.1 33 | plotly-express==0.4.1 34 | protobuf==3.20.3 35 | pyarrow==11.0.0 36 | pycparser==2.21 37 | pydeck==0.8.0 38 | Pygments==2.15.0 39 | Pympler==1.0.1 40 | pyrsistent==0.19.3 41 | python-dateutil==2.8.2 42 | pytz==2023.3 43 | pytz-deprecation-shim==0.1.0.post0 44 | requests==2.28.2 45 | requests-futures==1.0.0 46 | rich==13.3.3 47 | scipy==1.10.1 48 | six==1.16.0 49 | smmap==5.0.0 50 | soupsieve==2.4 51 | statsmodels==0.13.5 52 | streamlit==1.21.0 53 | TA-Lib==0.4.26 54 | tablib==3.4.0 55 | tenacity==8.2.2 56 | toml==0.10.2 57 | toolz==0.12.0 58 | tornado==6.2 59 | tqdm==4.65.0 60 | typing_extensions==4.5.0 61 | tzdata==2023.3 62 | tzlocal==4.3 63 | urllib3==1.26.15 64 | validators==0.20.0 65 | webencodings==0.5.1 66 | yahooquery==2.3.1 67 | yfinance==0.2.17 68 | zipp==3.15.0 69 | --------------------------------------------------------------------------------