├── Dockerfile ├── utils ├── __init__.py ├── charting_utils.py ├── jdk_rs_utils.py └── yf_utils.py ├── .vscode └── settings.json ├── README.md ├── app.py ├── .gitignore └── eda └── RRG.ipynb /Dockerfile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "jupyter.jupyterServerType": "local" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | https://quant.stackexchange.com/a/42330 4 | https://school.stockcharts.com/doku.php?id=chart_analysis:rrg_charts 5 | https://marginstone.com/how-to-create-a-relative-rotation-graph-on-excel/ 6 | -------------------------------------------------------------------------------- /utils/charting_utils.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def get_jdk_charts(): 4 | """ 5 | Given a pandas dataframe of 6 | """ 7 | 8 | 9 | def get_spdr_sector_industries(): 10 | 11 | spdr_tickers = [ 12 | 'XLB', 13 | 'XLE', 14 | 'XLF', 15 | 'XLI', 16 | 'XLK', 17 | 'XLP', 18 | 'XLRE', 19 | 'XLU', 20 | 'XLV', 21 | 'XLY' 22 | ] 23 | -------------------------------------------------------------------------------- /utils/jdk_rs_utils.py: -------------------------------------------------------------------------------- 1 | 2 | def rs_ratio(prices_df, benchmark, window=1): 3 | 4 | for series in prices_df: 5 | rs = (prices_df[series].divide(benchmark)) * 100 6 | rs_ratio = rs.rolling(window).mean() 7 | rel_ratio = 100 + ((rs_ratio - rs_ratio.mean()) / rs_ratio.std() + 1) 8 | prices_df[f'{series}_rs'] = rel_ratio 9 | prices_df.dropna(axis=0, how='all', inplace=True) 10 | 11 | return prices_df 12 | 13 | def rs_momentum(prices_df, window=1): 14 | 15 | for series in prices_df: 16 | rm = (prices_df[series].pct_change()) * 100 17 | rm_ratio = rm.rolling(window).mean() 18 | rel_ratio = 100 + ((rm_ratio - rm_ratio.mean()) / rm_ratio.std() + 1) 19 | prices_df[f'{series}_rm'] = rel_ratio 20 | prices_df.dropna(axis=0, how='all', inplace=True) 21 | 22 | return prices_df 23 | -------------------------------------------------------------------------------- /utils/yf_utils.py: -------------------------------------------------------------------------------- 1 | import yfinance as yf 2 | import pandas as pd 3 | 4 | def get_tickers_dataframe(tickers:list, period:str = '1mo', interval:str = '1d')-> pd.DataFrame: 5 | """ 6 | Fetch ticker symbol data from yahoo finance. 7 | 8 | KWARGS 9 | tickers -- list of tickers to fetch information for 10 | period -- (optional, default is '1mo') data retrival time horizon 11 | valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max 12 | interval -- (optional, default is '1d') fetch data by interval (including intraday if period < 60 days) 13 | valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo 14 | """ 15 | data = yf.download( # or pdr.get_data_yahoo(... 16 | # tickers list or string as well 17 | tickers = " ".join(tickers), 18 | 19 | # use "period" instead of start/end 20 | # valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max 21 | # (optional, default is '1mo') 22 | period = period, 23 | 24 | # fetch data by interval (including intraday if period < 60 days) 25 | # valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo 26 | # (optional, default is '1d') 27 | interval = interval, 28 | 29 | # group by ticker (to access via data['SPY']) 30 | # (optional, default is 'column') 31 | group_by = 'ticker', 32 | 33 | # adjust all OHLC automatically 34 | # (optional, default is False) 35 | auto_adjust = True, 36 | 37 | # use threads for mass downloading? (True/False/Integer) 38 | # (optional, default is True) 39 | threads = True, 40 | 41 | # proxy URL scheme use use when downloading? 42 | # (optional, default is None) 43 | proxy = None 44 | ) 45 | 46 | return data 47 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import yfinance as yf 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | from numpy import mean, std 6 | from utils.yf_utils import get_tickers_dataframe 7 | from utils.jdk_rs_utils import rs_ratio, rs_momentum 8 | 9 | st.title('Relative Rotation Graph w $SPY benchmark') 10 | 11 | @st.cache 12 | def load_data(): 13 | 14 | spdr_tickers = [ 15 | 'XLB', 16 | 'XLE', 17 | 'XLF', 18 | 'XLI', 19 | 'XLK', 20 | 'XLP', 21 | 'XLRE', 22 | 'XLU', 23 | 'XLV', 24 | 'XLY', 25 | 'SPY' 26 | ] 27 | 28 | data = get_tickers_dataframe(tickers=spdr_tickers) 29 | 30 | prices_df = pd.DataFrame() 31 | prices_df['XLF'] = data['XLF']['Close'] 32 | prices_df['XLE'] = data['XLE']['Close'] 33 | 34 | rs_ratio_df = rs_ratio(prices_df, data['SPY']['Close']) 35 | rm_momentum_df = rs_momentum(prices_df) 36 | 37 | return rs_ratio_df, rm_momentum_df 38 | 39 | ratio, momentum = load_data() 40 | 41 | st.subheader('JdK RS-Ratio') 42 | st.dataframe(ratio) 43 | 44 | print(ratio.columns) 45 | 46 | st.subheader('JdK RS-Momentum') 47 | st.dataframe(momentum) 48 | 49 | fig = plt.figure() 50 | ax = fig.add_subplot(1, 1, 1) 51 | 52 | # Move left y-axis and bottim x-axis to centre, passing through (0,0) 53 | ax.spines['left'].set_position('center') 54 | ax.spines['bottom'].set_position('center') 55 | 56 | # Eliminate upper and right axes 57 | ax.spines['right'].set_color('none') 58 | ax.spines['top'].set_color('none') 59 | 60 | # Show ticks in the left and lower axes only 61 | ax.xaxis.set_ticks_position('bottom') 62 | ax.yaxis.set_ticks_position('left') 63 | 64 | 65 | ax.plot(ratio['XLF_rs'], momentum['XLF_rm'], '-o', label='XLF') 66 | ax.scatter(ratio['XLE_rs'][-1],momentum['XLE_rm'][-1], marker='s') 67 | 68 | ax.plot(ratio['XLE_rs'], momentum['XLE_rm'], '-o', label='XLE') 69 | ax.scatter(ratio['XLE_rs'][-1],momentum['XLE_rm'][-1], marker='s') 70 | 71 | st.pyplot(fig) 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | -------------------------------------------------------------------------------- /eda/RRG.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Collecting yfinance\n", 13 | " Downloading yfinance-0.1.59.tar.gz (25 kB)\n", 14 | "Requirement already satisfied: pandas>=0.24 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from yfinance) (0.24.0)\n", 15 | "Requirement already satisfied: numpy>=1.15 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from yfinance) (1.15.0)\n", 16 | "Requirement already satisfied: requests>=2.20 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from yfinance) (2.25.1)\n", 17 | "Collecting multitasking>=0.0.7\n", 18 | " Downloading multitasking-0.0.9.tar.gz (8.1 kB)\n", 19 | "Collecting lxml>=4.5.1\n", 20 | " Downloading lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl (5.5 MB)\n", 21 | "\u001b[K |████████████████████████████████| 5.5 MB 6.8 MB/s eta 0:00:01\n", 22 | "\u001b[?25hRequirement already satisfied: pytz>=2011k in /home/sparqy/anaconda3/lib/python3.7/site-packages (from pandas>=0.24->yfinance) (2021.1)\n", 23 | "Requirement already satisfied: python-dateutil>=2.5.0 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from pandas>=0.24->yfinance) (2.7.3)\n", 24 | "Requirement already satisfied: idna<3,>=2.5 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from requests>=2.20->yfinance) (2.10)\n", 25 | "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from requests>=2.20->yfinance) (1.26.4)\n", 26 | "Requirement already satisfied: chardet<5,>=3.0.2 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from requests>=2.20->yfinance) (4.0.0)\n", 27 | "Requirement already satisfied: certifi>=2017.4.17 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from requests>=2.20->yfinance) (2020.12.5)\n", 28 | "Requirement already satisfied: six>=1.5 in /home/sparqy/anaconda3/lib/python3.7/site-packages (from python-dateutil>=2.5.0->pandas>=0.24->yfinance) (1.15.0)\n", 29 | "Building wheels for collected packages: yfinance, multitasking\n", 30 | " Building wheel for yfinance (setup.py) ... \u001b[?25ldone\n", 31 | "\u001b[?25h Created wheel for yfinance: filename=yfinance-0.1.59-py2.py3-none-any.whl size=23442 sha256=3336827bc7e52be8c5cf2084d0192691c36a2dd73e6e7fd2be6145c673fd4f52\n", 32 | " Stored in directory: /home/sparqy/.cache/pip/wheels/26/af/8b/fac1b47dffef567f945641cdc9b67bb25fae5725d462a8cf81\n", 33 | " Building wheel for multitasking (setup.py) ... \u001b[?25ldone\n", 34 | "\u001b[?25h Created wheel for multitasking: filename=multitasking-0.0.9-py3-none-any.whl size=8366 sha256=4915b123270e12939d2a69117b8b7f805ee83923cb995815eca82e0381df0902\n", 35 | " Stored in directory: /home/sparqy/.cache/pip/wheels/ae/25/47/4d68431a7ec1b6c4b5233365934b74c1d4e665bf5f968d363a\n", 36 | "Successfully built yfinance multitasking\n", 37 | "Installing collected packages: multitasking, lxml, yfinance\n", 38 | " Attempting uninstall: lxml\n", 39 | " Found existing installation: lxml 4.5.0\n", 40 | " Uninstalling lxml-4.5.0:\n", 41 | " Successfully uninstalled lxml-4.5.0\n", 42 | "Successfully installed lxml-4.6.3 multitasking-0.0.9 yfinance-0.1.59\n" 43 | ] 44 | } 45 | ], 46 | "source": [ 47 | "!pip install yfinance" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 2, 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "output_type": "execute_result", 57 | "data": { 58 | "text/plain": [ 59 | " Open High Low Close Volume Dividends \\\n", 60 | "count 21.000000 21.000000 21.000000 21.000000 2.100000e+01 21.0 \n", 61 | "mean 22.436409 22.512701 22.354999 22.440595 2.190000e+06 0.0 \n", 62 | "std 0.358932 0.338055 0.361026 0.359019 9.428644e+05 0.0 \n", 63 | "min 21.931673 22.048900 21.697211 21.746058 9.129000e+05 0.0 \n", 64 | "25% 22.156360 22.234514 22.097747 22.205206 1.564900e+06 0.0 \n", 65 | "50% 22.351742 22.410358 22.273590 22.322435 1.959500e+06 0.0 \n", 66 | "75% 22.898813 22.908583 22.664355 22.742508 2.420400e+06 0.0 \n", 67 | "max 22.957428 22.996507 22.898814 22.996506 4.481500e+06 0.0 \n", 68 | "\n", 69 | " Stock Splits \n", 70 | "count 21.0 \n", 71 | "mean 0.0 \n", 72 | "std 0.0 \n", 73 | "min 0.0 \n", 74 | "25% 0.0 \n", 75 | "50% 0.0 \n", 76 | "75% 0.0 \n", 77 | "max 0.0 " 78 | ], 79 | "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
OpenHighLowCloseVolumeDividendsStock Splits
count21.00000021.00000021.00000021.0000002.100000e+0121.021.0
mean22.43640922.51270122.35499922.4405952.190000e+060.00.0
std0.3589320.3380550.3610260.3590199.428644e+050.00.0
min21.93167322.04890021.69721121.7460589.129000e+050.00.0
25%22.15636022.23451422.09774722.2052061.564900e+060.00.0
50%22.35174222.41035822.27359022.3224351.959500e+060.00.0
75%22.89881322.90858322.66435522.7425082.420400e+060.00.0
max22.95742822.99650722.89881422.9965064.481500e+060.00.0
\n
" 80 | }, 81 | "metadata": {}, 82 | "execution_count": 2 83 | } 84 | ], 85 | "source": [ 86 | "import yfinance as yf\n", 87 | "SPY = yf.Ticker('EWA')\n", 88 | "\n", 89 | "spy_df = SPY.history(start=\"2020-01-01\", end=\"2020-02-01\")\n", 90 | "spy_df.describe()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 16, 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "output_type": "execute_result", 100 | "data": { 101 | "text/plain": [ 102 | " Open High Low Close Volume \\\n", 103 | "Date \n", 104 | "2020-01-02 316.571857 317.892788 315.583600 317.873199 59151200 \n", 105 | "2020-01-03 314.243138 316.669737 314.184433 315.466217 77709700 \n", 106 | "2020-01-06 313.587557 316.757797 313.460352 316.669739 55653900 \n", 107 | "2020-01-07 316.063060 316.571880 315.299860 315.779327 40496400 \n", 108 | "2020-01-08 315.984822 318.763653 315.720648 317.462311 68296000 \n", 109 | "2020-01-09 319.135431 319.693162 318.509200 319.614868 48473300 \n", 110 | "2020-01-10 320.241117 320.407439 318.196134 318.695129 53029300 \n", 111 | "2020-01-13 319.360472 320.896636 318.900593 320.886871 47086800 \n", 112 | "2020-01-14 320.417233 321.542459 319.800797 320.397675 62832800 \n", 113 | "2020-01-15 320.299799 321.933815 320.211741 321.121704 72056600 \n", 114 | "2020-01-16 322.599212 323.792938 322.354597 323.792938 54050300 \n", 115 | "2020-01-17 324.556166 325.025809 323.724466 324.800781 95846000 \n", 116 | "2020-01-21 323.773385 325.025817 323.695121 324.164764 77742400 \n", 117 | "2020-01-22 325.084468 325.779198 324.037535 324.203857 48914900 \n", 118 | "2020-01-23 323.509193 325.016034 322.315467 324.575714 51963000 \n", 119 | "2020-01-24 325.280181 325.368239 320.309573 321.689209 87578400 \n", 120 | "2020-01-27 316.072867 318.117850 315.710840 316.532745 84062500 \n", 121 | "2020-01-28 318.059128 320.789047 316.630580 319.849731 63834000 \n", 122 | "2020-01-29 321.307614 321.552230 319.370247 319.585510 53888900 \n", 123 | "2020-01-30 317.374207 320.847768 316.571890 320.622711 75491800 \n", 124 | "2020-01-31 319.957361 320.123713 313.822410 314.800873 113845600 \n", 125 | "\n", 126 | " Dividends Stock Splits \n", 127 | "Date \n", 128 | "2020-01-02 0 0 \n", 129 | "2020-01-03 0 0 \n", 130 | "2020-01-06 0 0 \n", 131 | "2020-01-07 0 0 \n", 132 | "2020-01-08 0 0 \n", 133 | "2020-01-09 0 0 \n", 134 | "2020-01-10 0 0 \n", 135 | "2020-01-13 0 0 \n", 136 | "2020-01-14 0 0 \n", 137 | "2020-01-15 0 0 \n", 138 | "2020-01-16 0 0 \n", 139 | "2020-01-17 0 0 \n", 140 | "2020-01-21 0 0 \n", 141 | "2020-01-22 0 0 \n", 142 | "2020-01-23 0 0 \n", 143 | "2020-01-24 0 0 \n", 144 | "2020-01-27 0 0 \n", 145 | "2020-01-28 0 0 \n", 146 | "2020-01-29 0 0 \n", 147 | "2020-01-30 0 0 \n", 148 | "2020-01-31 0 0 " 149 | ], 150 | "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
OpenHighLowCloseVolumeDividendsStock Splits
Date
2020-01-02316.571857317.892788315.583600317.8731995915120000
2020-01-03314.243138316.669737314.184433315.4662177770970000
2020-01-06313.587557316.757797313.460352316.6697395565390000
2020-01-07316.063060316.571880315.299860315.7793274049640000
2020-01-08315.984822318.763653315.720648317.4623116829600000
2020-01-09319.135431319.693162318.509200319.6148684847330000
2020-01-10320.241117320.407439318.196134318.6951295302930000
2020-01-13319.360472320.896636318.900593320.8868714708680000
2020-01-14320.417233321.542459319.800797320.3976756283280000
2020-01-15320.299799321.933815320.211741321.1217047205660000
2020-01-16322.599212323.792938322.354597323.7929385405030000
2020-01-17324.556166325.025809323.724466324.8007819584600000
2020-01-21323.773385325.025817323.695121324.1647647774240000
2020-01-22325.084468325.779198324.037535324.2038574891490000
2020-01-23323.509193325.016034322.315467324.5757145196300000
2020-01-24325.280181325.368239320.309573321.6892098757840000
2020-01-27316.072867318.117850315.710840316.5327458406250000
2020-01-28318.059128320.789047316.630580319.8497316383400000
2020-01-29321.307614321.552230319.370247319.5855105388890000
2020-01-30317.374207320.847768316.571890320.6227117549180000
2020-01-31319.957361320.123713313.822410314.80087311384560000
\n
" 151 | }, 152 | "metadata": {}, 153 | "execution_count": 16 154 | } 155 | ], 156 | "source": [ 157 | "\n", 158 | "tickers = yf.Tickers('SPY aapl goog')\n", 159 | "# ^ returns a named tuple of Ticker objects\n", 160 | "\n", 161 | "tickers.tickers['SPY'].history(start='2020-01-01', end='2020-02-01')\n", 162 | "# access each ticker using (example)\n", 163 | "# tickers.tickers.MSFT.info\n", 164 | "# tickers.tickers.AAPL.history(period=\"1mo\")\n", 165 | "# tickers.tickers.GOOG.actions" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 17, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "spdr_tickers = ['XLB', 'XLE', 'XLF', 'XLI','XLK','XLP','XLRE','XLU','XLV', 'XLY']" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 19, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "import pandas as pd \n", 184 | "\n", 185 | "def get_tickers_dataframe(tickers:list, period:str = '1mo', interval:str = '1d')-> pd.DataFrame:\n", 186 | " \"\"\"\n", 187 | " Fetch ticker symbol data from yahoo finance.\n", 188 | "\n", 189 | " KWARGS\n", 190 | " tickers -- list of tickers to fetch information for\n", 191 | " period -- (optional, default is '1mo') data retrival time horizon\n", 192 | " valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max\n", 193 | " interval -- (optional, default is '1d') fetch data by interval (including intraday if period < 60 days)\n", 194 | " valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo\n", 195 | " \"\"\"\n", 196 | " data = yf.download( # or pdr.get_data_yahoo(...\n", 197 | " # tickers list or string as well\n", 198 | " tickers = \" \".join(tickers),\n", 199 | "\n", 200 | " # use \"period\" instead of start/end\n", 201 | " # valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max\n", 202 | " # (optional, default is '1mo')\n", 203 | " period = period,\n", 204 | "\n", 205 | " # fetch data by interval (including intraday if period < 60 days)\n", 206 | " # valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo\n", 207 | " # (optional, default is '1d')\n", 208 | " interval = interval,\n", 209 | "\n", 210 | " # group by ticker (to access via data['SPY'])\n", 211 | " # (optional, default is 'column')\n", 212 | " group_by = 'ticker',\n", 213 | "\n", 214 | " # adjust all OHLC automatically\n", 215 | " # (optional, default is False)\n", 216 | " auto_adjust = True,\n", 217 | "\n", 218 | " # use threads for mass downloading? (True/False/Integer)\n", 219 | " # (optional, default is True)\n", 220 | " threads = True,\n", 221 | "\n", 222 | " # proxy URL scheme use use when downloading?\n", 223 | " # (optional, default is None)\n", 224 | " proxy = None\n", 225 | " )\n", 226 | "\n", 227 | " return data" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [] 236 | } 237 | ], 238 | "metadata": { 239 | "kernelspec": { 240 | "name": "python376jvsc74a57bd0b60fcd99ce9cba0e409542ff496cb92871fe949fa612d84becdd519cb9978a21", 241 | "display_name": "Python 3.7.6 64-bit ('base': conda)" 242 | }, 243 | "language_info": { 244 | "codemirror_mode": { 245 | "name": "ipython", 246 | "version": 3 247 | }, 248 | "file_extension": ".py", 249 | "mimetype": "text/x-python", 250 | "name": "python", 251 | "nbconvert_exporter": "python", 252 | "pygments_lexer": "ipython3", 253 | "version": "3.7.6" 254 | } 255 | }, 256 | "nbformat": 4, 257 | "nbformat_minor": 4 258 | } --------------------------------------------------------------------------------