├── 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 Open | \n High | \n Low | \n Close | \n Volume | \n Dividends | \n Stock Splits | \n
\n \n \n \n | count | \n 21.000000 | \n 21.000000 | \n 21.000000 | \n 21.000000 | \n 2.100000e+01 | \n 21.0 | \n 21.0 | \n
\n \n | mean | \n 22.436409 | \n 22.512701 | \n 22.354999 | \n 22.440595 | \n 2.190000e+06 | \n 0.0 | \n 0.0 | \n
\n \n | std | \n 0.358932 | \n 0.338055 | \n 0.361026 | \n 0.359019 | \n 9.428644e+05 | \n 0.0 | \n 0.0 | \n
\n \n | min | \n 21.931673 | \n 22.048900 | \n 21.697211 | \n 21.746058 | \n 9.129000e+05 | \n 0.0 | \n 0.0 | \n
\n \n | 25% | \n 22.156360 | \n 22.234514 | \n 22.097747 | \n 22.205206 | \n 1.564900e+06 | \n 0.0 | \n 0.0 | \n
\n \n | 50% | \n 22.351742 | \n 22.410358 | \n 22.273590 | \n 22.322435 | \n 1.959500e+06 | \n 0.0 | \n 0.0 | \n
\n \n | 75% | \n 22.898813 | \n 22.908583 | \n 22.664355 | \n 22.742508 | \n 2.420400e+06 | \n 0.0 | \n 0.0 | \n
\n \n | max | \n 22.957428 | \n 22.996507 | \n 22.898814 | \n 22.996506 | \n 4.481500e+06 | \n 0.0 | \n 0.0 | \n
\n \n
\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 Open | \n High | \n Low | \n Close | \n Volume | \n Dividends | \n Stock Splits | \n
\n \n | Date | \n | \n | \n | \n | \n | \n | \n | \n
\n \n \n \n | 2020-01-02 | \n 316.571857 | \n 317.892788 | \n 315.583600 | \n 317.873199 | \n 59151200 | \n 0 | \n 0 | \n
\n \n | 2020-01-03 | \n 314.243138 | \n 316.669737 | \n 314.184433 | \n 315.466217 | \n 77709700 | \n 0 | \n 0 | \n
\n \n | 2020-01-06 | \n 313.587557 | \n 316.757797 | \n 313.460352 | \n 316.669739 | \n 55653900 | \n 0 | \n 0 | \n
\n \n | 2020-01-07 | \n 316.063060 | \n 316.571880 | \n 315.299860 | \n 315.779327 | \n 40496400 | \n 0 | \n 0 | \n
\n \n | 2020-01-08 | \n 315.984822 | \n 318.763653 | \n 315.720648 | \n 317.462311 | \n 68296000 | \n 0 | \n 0 | \n
\n \n | 2020-01-09 | \n 319.135431 | \n 319.693162 | \n 318.509200 | \n 319.614868 | \n 48473300 | \n 0 | \n 0 | \n
\n \n | 2020-01-10 | \n 320.241117 | \n 320.407439 | \n 318.196134 | \n 318.695129 | \n 53029300 | \n 0 | \n 0 | \n
\n \n | 2020-01-13 | \n 319.360472 | \n 320.896636 | \n 318.900593 | \n 320.886871 | \n 47086800 | \n 0 | \n 0 | \n
\n \n | 2020-01-14 | \n 320.417233 | \n 321.542459 | \n 319.800797 | \n 320.397675 | \n 62832800 | \n 0 | \n 0 | \n
\n \n | 2020-01-15 | \n 320.299799 | \n 321.933815 | \n 320.211741 | \n 321.121704 | \n 72056600 | \n 0 | \n 0 | \n
\n \n | 2020-01-16 | \n 322.599212 | \n 323.792938 | \n 322.354597 | \n 323.792938 | \n 54050300 | \n 0 | \n 0 | \n
\n \n | 2020-01-17 | \n 324.556166 | \n 325.025809 | \n 323.724466 | \n 324.800781 | \n 95846000 | \n 0 | \n 0 | \n
\n \n | 2020-01-21 | \n 323.773385 | \n 325.025817 | \n 323.695121 | \n 324.164764 | \n 77742400 | \n 0 | \n 0 | \n
\n \n | 2020-01-22 | \n 325.084468 | \n 325.779198 | \n 324.037535 | \n 324.203857 | \n 48914900 | \n 0 | \n 0 | \n
\n \n | 2020-01-23 | \n 323.509193 | \n 325.016034 | \n 322.315467 | \n 324.575714 | \n 51963000 | \n 0 | \n 0 | \n
\n \n | 2020-01-24 | \n 325.280181 | \n 325.368239 | \n 320.309573 | \n 321.689209 | \n 87578400 | \n 0 | \n 0 | \n
\n \n | 2020-01-27 | \n 316.072867 | \n 318.117850 | \n 315.710840 | \n 316.532745 | \n 84062500 | \n 0 | \n 0 | \n
\n \n | 2020-01-28 | \n 318.059128 | \n 320.789047 | \n 316.630580 | \n 319.849731 | \n 63834000 | \n 0 | \n 0 | \n
\n \n | 2020-01-29 | \n 321.307614 | \n 321.552230 | \n 319.370247 | \n 319.585510 | \n 53888900 | \n 0 | \n 0 | \n
\n \n | 2020-01-30 | \n 317.374207 | \n 320.847768 | \n 316.571890 | \n 320.622711 | \n 75491800 | \n 0 | \n 0 | \n
\n \n | 2020-01-31 | \n 319.957361 | \n 320.123713 | \n 313.822410 | \n 314.800873 | \n 113845600 | \n 0 | \n 0 | \n
\n \n
\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 | }
--------------------------------------------------------------------------------