├── .gitignore
├── README.md
├── finished_files
├── 001_equal_weight_S&P_500.ipynb
├── 002_quantitative_momentum_strategy.ipynb
└── 003_quantitative_value_strategy.ipynb
├── requirements.txt
└── starter_files
├── 001_equal_weight_S&P_500.ipynb
├── 002_quantitative_momentum_strategy.ipynb
└── 003_quantitative_value_strategy.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | algorithmic-trading/
2 | .ipynb_checkpoints/
3 | sp_500_stocks.csv
4 | **/secrets.py
5 | .DS_Store
6 | __pycache__
7 | **/recommended_trades.xlsx
8 | **/momentum_strategy.xlsx
9 | venv/
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Algorithmic Trading in Python
2 |
3 | This repository
4 |
5 | ## Course Outline
6 |
7 | * Section 1: Algorithmic Trading Fundamentals
8 | * What is Algorithmic Trading?
9 | * The Differences Between Real-World Algorithmic Trading and This Course
10 | * Section 2: Course Configuration & API Basics
11 | * How to Install Python
12 | * Cloning The Repository & Installing Our Dependencies
13 | * Jupyter Notebook Basics
14 | * The Basics of API Requests
15 | * Section 3: Building An Equal-Weight S&P 500 Index Fund
16 | * Theory & Concepts
17 | * Importing our Constituents
18 | * Pulling Data For Our Constituents
19 | * Calculating Weights
20 | * Generating Our Output File
21 | * Additional Project Ideas
22 | * Section 4: Building A Quantitative Momentum Investing Strategy
23 | * Theory & Concepts
24 | * Pulling Data For Our Constituents
25 | * Calculating Weights
26 | * Generating Our Output File
27 | * Additional Project Ideas
28 | * Section 5: Building A Quantitative Value Investing Strategy
29 | * Theory & Concepts
30 | * Importing our Constituents
31 | * Pulling Data For Our Constituents
32 | * Calculating Weights
33 | * Generating Our Output File
34 | * Additional Project Ideas
35 |
--------------------------------------------------------------------------------
/finished_files/001_equal_weight_S&P_500.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Equal-Weight S&P 500 Index Fund\n",
8 | "\n",
9 | "## Introduction & Library Imports\n",
10 | "\n",
11 | "The S&P 500 is the world's most popular stock market index. The largest fund that is benchmarked to this index is the SPDR® S&P 500® ETF Trust. It has more than US$250 billion of assets under management.\n",
12 | "\n",
13 | "The goal of this section of the course is to create a Python script that will accept the value of your portfolio and tell you how many shares of each S&P 500 constituent you should purchase to get an equal-weight version of the index fund.\n",
14 | "\n",
15 | "## Library Imports\n",
16 | "\n",
17 | "The first thing we need to do is import the open-source software libraries that we'll be using in this tutorial."
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": null,
23 | "metadata": {},
24 | "outputs": [],
25 | "source": [
26 | "import numpy as np #The Numpy numerical computing library\n",
27 | "import pandas as pd #The Pandas data science library\n",
28 | "import requests #The requests library for HTTP requests in Python\n",
29 | "import xlsxwriter #The XlsxWriter libarary for \n",
30 | "import math #The Python math module"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Importing Our List of Stocks\n",
38 | "\n",
39 | "The next thing we need to do is import the constituents of the S&P 500.\n",
40 | "\n",
41 | "These constituents change over time, so in an ideal world you would connect directly to the index provider (Standard & Poor's) and pull their real-time constituents on a regular basis.\n",
42 | "\n",
43 | "Paying for access to the index provider's API is outside of the scope of this course. \n",
44 | "\n",
45 | "There's a static version of the S&P 500 constituents available here. [Click this link to download them now](http://nickmccullum.com/algorithmic-trading-python/sp_500_stocks.csv). Move this file into the `starter-files` folder so it can be accessed by other files in that directory.\n",
46 | "\n",
47 | "Now it's time to import these stocks to our Jupyter Notebook file."
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": null,
53 | "metadata": {},
54 | "outputs": [],
55 | "source": [
56 | "stocks = pd.read_csv('sp_500_stocks.csv')"
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "metadata": {},
62 | "source": [
63 | "## Acquiring an API Token\n",
64 | "\n",
65 | "Now it's time to import our IEX Cloud API token. This is the data provider that we will be using throughout this course.\n",
66 | "\n",
67 | "API tokens (and other sensitive information) should be stored in a `secrets.py` file that doesn't get pushed to your local Git repository. We'll be using a sandbox API token in this course, which means that the data we'll use is randomly-generated and (more importantly) has no cost associated with it.\n",
68 | "\n",
69 | "[Click here](http://nickmccullum.com/algorithmic-trading-python/secrets.py) to download your `secrets.py` file. Move the file into the same directory as this Jupyter Notebook before proceeding."
70 | ]
71 | },
72 | {
73 | "cell_type": "code",
74 | "execution_count": null,
75 | "metadata": {},
76 | "outputs": [],
77 | "source": [
78 | "from secrets import IEX_CLOUD_API_TOKEN"
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "metadata": {},
84 | "source": [
85 | "## Making Our First API Call\n",
86 | "\n",
87 | "Now it's time to structure our API calls to IEX cloud. \n",
88 | "\n",
89 | "We need the following information from the API:\n",
90 | "\n",
91 | "* Market capitalization for each stock\n",
92 | "* Price of each stock\n",
93 | "\n"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "symbol='AAPL'\n",
103 | "api_url = f'https://sandbox.iexapis.com/stable/stock/{symbol}/quote?token={IEX_CLOUD_API_TOKEN}'\n",
104 | "data = requests.get(api_url).json()\n",
105 | "data"
106 | ]
107 | },
108 | {
109 | "cell_type": "markdown",
110 | "metadata": {},
111 | "source": [
112 | "## Parsing Our API Call\n",
113 | "\n",
114 | "The API call that we executed in the last code block contains all of the information required to build our equal-weight S&P 500 strategy. \n",
115 | "\n",
116 | "With that said, the data isn't in a proper format yet. We need to parse it first."
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": null,
122 | "metadata": {},
123 | "outputs": [],
124 | "source": [
125 | "data['latestPrice']\n",
126 | "data['marketCap']"
127 | ]
128 | },
129 | {
130 | "cell_type": "markdown",
131 | "metadata": {},
132 | "source": [
133 | "## Adding Our Stocks Data to a Pandas DataFrame\n",
134 | "\n",
135 | "The next thing we need to do is add our stock's price and market capitalization to a pandas DataFrame. Think of a DataFrame like the Python version of a spreadsheet. It stores tabular data."
136 | ]
137 | },
138 | {
139 | "cell_type": "code",
140 | "execution_count": null,
141 | "metadata": {},
142 | "outputs": [],
143 | "source": [
144 | "my_columns = ['Ticker', 'Price','Market Capitalization', 'Number Of Shares to Buy']\n",
145 | "final_dataframe = pd.DataFrame(columns = my_columns)\n",
146 | "final_dataframe"
147 | ]
148 | },
149 | {
150 | "cell_type": "code",
151 | "execution_count": null,
152 | "metadata": {},
153 | "outputs": [],
154 | "source": [
155 | "final_dataframe = final_dataframe.append(\n",
156 | " pd.Series(['AAPL', \n",
157 | " data['latestPrice'], \n",
158 | " data['marketCap'], \n",
159 | " 'N/A'], \n",
160 | " index = my_columns), \n",
161 | " ignore_index = True)\n",
162 | "final_dataframe"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "metadata": {},
168 | "source": [
169 | "## Looping Through The Tickers in Our List of Stocks\n",
170 | "\n",
171 | "Using the same logic that we outlined above, we can pull data for all S&P 500 stocks and store their data in the DataFrame using a `for` loop."
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": null,
177 | "metadata": {},
178 | "outputs": [],
179 | "source": [
180 | "final_dataframe = pd.DataFrame(columns = my_columns)\n",
181 | "for symbol in stocks['Ticker']:\n",
182 | " api_url = f'https://sandbox.iexapis.com/stable/stock/{symbol}/quote?token={IEX_CLOUD_API_TOKEN}'\n",
183 | " data = requests.get(api_url).json()\n",
184 | " final_dataframe = final_dataframe.append(\n",
185 | " pd.Series([symbol, \n",
186 | " data['latestPrice'], \n",
187 | " data['marketCap'], \n",
188 | " 'N/A'], \n",
189 | " index = my_columns), \n",
190 | " ignore_index = True)\n"
191 | ]
192 | },
193 | {
194 | "cell_type": "code",
195 | "execution_count": null,
196 | "metadata": {},
197 | "outputs": [],
198 | "source": [
199 | "final_dataframe"
200 | ]
201 | },
202 | {
203 | "cell_type": "markdown",
204 | "metadata": {},
205 | "source": [
206 | "## Using Batch API Calls to Improve Performance\n",
207 | "\n",
208 | "Batch API calls are one of the easiest ways to improve the performance of your code.\n",
209 | "\n",
210 | "This is because HTTP requests are typically one of the slowest components of a script.\n",
211 | "\n",
212 | "Also, API providers will often give you discounted rates for using batch API calls since they are easier for the API provider to respond to.\n",
213 | "\n",
214 | "IEX Cloud limits their batch API calls to 100 tickers per request. Still, this reduces the number of API calls we'll make in this section from 500 to 5 - huge improvement! In this section, we'll split our list of stocks into groups of 100 and then make a batch API call for each group."
215 | ]
216 | },
217 | {
218 | "cell_type": "code",
219 | "execution_count": null,
220 | "metadata": {},
221 | "outputs": [],
222 | "source": [
223 | "# Function sourced from \n",
224 | "# https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks\n",
225 | "def chunks(lst, n):\n",
226 | " \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n",
227 | " for i in range(0, len(lst), n):\n",
228 | " yield lst[i:i + n]"
229 | ]
230 | },
231 | {
232 | "cell_type": "code",
233 | "execution_count": null,
234 | "metadata": {},
235 | "outputs": [],
236 | "source": [
237 | "symbol_groups = list(chunks(stocks['Ticker'], 100))\n",
238 | "symbol_strings = []\n",
239 | "for i in range(0, len(symbol_groups)):\n",
240 | " symbol_strings.append(','.join(symbol_groups[i]))\n",
241 | "# print(symbol_strings[i])\n",
242 | "\n",
243 | "final_dataframe = pd.DataFrame(columns = my_columns)\n",
244 | "\n",
245 | "for symbol_string in symbol_strings:\n",
246 | "# print(symbol_strings)\n",
247 | " batch_api_call_url = f'https://sandbox.iexapis.com/stable/stock/market/batch/?types=quote&symbols={symbol_string}&token={IEX_CLOUD_API_TOKEN}'\n",
248 | " data = requests.get(batch_api_call_url).json()\n",
249 | " for symbol in symbol_string.split(','):\n",
250 | " final_dataframe = final_dataframe.append(\n",
251 | " pd.Series([symbol, \n",
252 | " data[symbol]['quote']['latestPrice'], \n",
253 | " data[symbol]['quote']['marketCap'], \n",
254 | " 'N/A'], \n",
255 | " index = my_columns), \n",
256 | " ignore_index = True)\n",
257 | " \n",
258 | " \n",
259 | "final_dataframe"
260 | ]
261 | },
262 | {
263 | "cell_type": "markdown",
264 | "metadata": {},
265 | "source": [
266 | "## Calculating the Number of Shares to Buy\n",
267 | "\n",
268 | "As you can see in the DataFrame above, we stil haven't calculated the number of shares of each stock to buy.\n",
269 | "\n",
270 | "We'll do that next."
271 | ]
272 | },
273 | {
274 | "cell_type": "code",
275 | "execution_count": null,
276 | "metadata": {},
277 | "outputs": [],
278 | "source": [
279 | "portfolio_size = input(\"Enter the value of your portfolio:\")\n",
280 | "\n",
281 | "try:\n",
282 | " val = float(portfolio_size)\n",
283 | "except ValueError:\n",
284 | " print(\"That's not a number! \\n Try again:\")\n",
285 | " portfolio_size = input(\"Enter the value of your portfolio:\")"
286 | ]
287 | },
288 | {
289 | "cell_type": "code",
290 | "execution_count": null,
291 | "metadata": {},
292 | "outputs": [],
293 | "source": [
294 | "position_size = float(portfolio_size) / len(final_dataframe.index)\n",
295 | "for i in range(0, len(final_dataframe['Ticker'])-1):\n",
296 | " final_dataframe.loc[i, 'Number Of Shares to Buy'] = math.floor(position_size / final_dataframe['Price'][i])\n",
297 | "final_dataframe"
298 | ]
299 | },
300 | {
301 | "cell_type": "markdown",
302 | "metadata": {},
303 | "source": [
304 | "## Formatting Our Excel Output\n",
305 | "\n",
306 | "We will be using the XlsxWriter library for Python to create nicely-formatted Excel files.\n",
307 | "\n",
308 | "XlsxWriter is an excellent package and offers tons of customization. However, the tradeoff for this is that the library can seem very complicated to new users. Accordingly, this section will be fairly long because I want to do a good job of explaining how XlsxWriter works.\n",
309 | "\n",
310 | "### Initializing our XlsxWriter Object"
311 | ]
312 | },
313 | {
314 | "cell_type": "code",
315 | "execution_count": null,
316 | "metadata": {},
317 | "outputs": [],
318 | "source": [
319 | "writer = pd.ExcelWriter('recommended_trades.xlsx', engine='xlsxwriter')\n",
320 | "final_dataframe.to_excel(writer, sheet_name='Recommended Trades', index = False)"
321 | ]
322 | },
323 | {
324 | "cell_type": "markdown",
325 | "metadata": {},
326 | "source": [
327 | "### Creating the Formats We'll Need For Our `.xlsx` File\n",
328 | "\n",
329 | "Formats include colors, fonts, and also symbols like `%` and `$`. We'll need four main formats for our Excel document:\n",
330 | "* String format for tickers\n",
331 | "* \\\\$XX.XX format for stock prices\n",
332 | "* \\\\$XX,XXX format for market capitalization\n",
333 | "* Integer format for the number of shares to purchase"
334 | ]
335 | },
336 | {
337 | "cell_type": "code",
338 | "execution_count": null,
339 | "metadata": {},
340 | "outputs": [],
341 | "source": [
342 | "background_color = '#0a0a23'\n",
343 | "font_color = '#ffffff'\n",
344 | "\n",
345 | "string_format = writer.book.add_format(\n",
346 | " {\n",
347 | " 'font_color': font_color,\n",
348 | " 'bg_color': background_color,\n",
349 | " 'border': 1\n",
350 | " }\n",
351 | " )\n",
352 | "\n",
353 | "dollar_format = writer.book.add_format(\n",
354 | " {\n",
355 | " 'num_format':'$0.00',\n",
356 | " 'font_color': font_color,\n",
357 | " 'bg_color': background_color,\n",
358 | " 'border': 1\n",
359 | " }\n",
360 | " )\n",
361 | "\n",
362 | "integer_format = writer.book.add_format(\n",
363 | " {\n",
364 | " 'num_format':'0',\n",
365 | " 'font_color': font_color,\n",
366 | " 'bg_color': background_color,\n",
367 | " 'border': 1\n",
368 | " }\n",
369 | " )"
370 | ]
371 | },
372 | {
373 | "cell_type": "markdown",
374 | "metadata": {},
375 | "source": [
376 | "### Applying the Formats to the Columns of Our `.xlsx` File\n",
377 | "\n",
378 | "We can use the `set_column` method applied to the `writer.sheets['Recommended Trades']` object to apply formats to specific columns of our spreadsheets.\n",
379 | "\n",
380 | "Here's an example:\n",
381 | "\n",
382 | "```python\n",
383 | "writer.sheets['Recommended Trades'].set_column('B:B', #This tells the method to apply the format to column B\n",
384 | " 18, #This tells the method to apply a column width of 18 pixels\n",
385 | " string_format #This applies the format 'string_format' to the column\n",
386 | " )\n",
387 | "```"
388 | ]
389 | },
390 | {
391 | "cell_type": "code",
392 | "execution_count": null,
393 | "metadata": {},
394 | "outputs": [],
395 | "source": [
396 | "# writer.sheets['Recommended Trades'].write('A1', 'Ticker', string_format)\n",
397 | "# writer.sheets['Recommended Trades'].write('B1', 'Price', string_format)\n",
398 | "# writer.sheets['Recommended Trades'].write('C1', 'Market Capitalization', string_format)\n",
399 | "# writer.sheets['Recommended Trades'].write('D1', 'Number Of Shares to Buy', string_format)\n",
400 | "# writer.sheets['Recommended Trades'].set_column('A:A', 20, string_format)\n",
401 | "# writer.sheets['Recommended Trades'].set_column('B:B', 20, dollar_format)\n",
402 | "# writer.sheets['Recommended Trades'].set_column('C:C', 20, dollar_format)\n",
403 | "# writer.sheets['Recommended Trades'].set_column('D:D', 20, integer_format)\n"
404 | ]
405 | },
406 | {
407 | "cell_type": "markdown",
408 | "metadata": {},
409 | "source": [
410 | "This code works, but it violates the software principle of \"Don't Repeat Yourself\". \n",
411 | "\n",
412 | "Let's simplify this by putting it in 2 loops:"
413 | ]
414 | },
415 | {
416 | "cell_type": "code",
417 | "execution_count": null,
418 | "metadata": {},
419 | "outputs": [],
420 | "source": [
421 | "column_formats = { \n",
422 | " 'A': ['Ticker', string_format],\n",
423 | " 'B': ['Price', dollar_format],\n",
424 | " 'C': ['Market Capitalization', dollar_format],\n",
425 | " 'D': ['Number of Shares to Buy', integer_format]\n",
426 | " }\n",
427 | "\n",
428 | "for column in column_formats.keys():\n",
429 | " writer.sheets['Recommended Trades'].set_column(f'{column}:{column}', 20, column_formats[column][1])\n",
430 | " writer.sheets['Recommended Trades'].write(f'{column}1', column_formats[column][0], string_format)"
431 | ]
432 | },
433 | {
434 | "cell_type": "markdown",
435 | "metadata": {},
436 | "source": [
437 | "## Saving Our Excel Output\n",
438 | "\n",
439 | "Saving our Excel file is very easy:"
440 | ]
441 | },
442 | {
443 | "cell_type": "code",
444 | "execution_count": null,
445 | "metadata": {},
446 | "outputs": [],
447 | "source": [
448 | "writer.save()"
449 | ]
450 | }
451 | ],
452 | "metadata": {
453 | "kernelspec": {
454 | "display_name": "Python 3",
455 | "language": "python",
456 | "name": "python3"
457 | },
458 | "language_info": {
459 | "codemirror_mode": {
460 | "name": "ipython",
461 | "version": 3
462 | },
463 | "file_extension": ".py",
464 | "mimetype": "text/x-python",
465 | "name": "python",
466 | "nbconvert_exporter": "python",
467 | "pygments_lexer": "ipython3",
468 | "version": "3.8.5"
469 | }
470 | },
471 | "nbformat": 4,
472 | "nbformat_minor": 4
473 | }
474 |
--------------------------------------------------------------------------------
/finished_files/002_quantitative_momentum_strategy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Quantitative Momentum Strategy\n",
8 | "\n",
9 | "\"Momentum investing\" means investing in the stocks that have increased in price the most.\n",
10 | "\n",
11 | "For this project, we're going to build an investing strategy that selects the 50 stocks with the highest price momentum. From there, we will calculate recommended trades for an equal-weight portfolio of these 50 stocks.\n",
12 | "\n",
13 | "\n",
14 | "## Library Imports\n",
15 | "\n",
16 | "The first thing we need to do is import the open-source software libraries that we'll be using in this tutorial."
17 | ]
18 | },
19 | {
20 | "cell_type": "code",
21 | "execution_count": 1,
22 | "metadata": {},
23 | "outputs": [],
24 | "source": [
25 | "import numpy as np #The Numpy numerical computing library\n",
26 | "import pandas as pd #The Pandas data science library\n",
27 | "import requests #The requests library for HTTP requests in Python\n",
28 | "import xlsxwriter #The XlsxWriter libarary for \n",
29 | "import math #The Python math module\n",
30 | "from scipy import stats #The SciPy stats module"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Importing Our List of Stocks\n",
38 | "\n",
39 | "As before, we'll need to import our list of stocks and our API token before proceeding. Make sure the `.csv` file is still in your working directory and import it with the following command:"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": 2,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "stocks = pd.read_csv('sp_500_stocks.csv')\n",
49 | "from secrets import IEX_CLOUD_API_TOKEN"
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "## Making Our First API Call\n",
57 | "\n",
58 | "It's now time to make the first version of our momentum screener!\n",
59 | "\n",
60 | "We need to get one-year price returns for each stock in the universe. Here's how."
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 3,
66 | "metadata": {},
67 | "outputs": [
68 | {
69 | "data": {
70 | "text/plain": [
71 | "{'week52change': 1.271858,\n",
72 | " 'week52high': 462.02,\n",
73 | " 'week52low': 206.85,\n",
74 | " 'marketcap': 1937104274094,\n",
75 | " 'employees': 137265,\n",
76 | " 'day200MovingAvg': 309.44,\n",
77 | " 'day50MovingAvg': 390.09,\n",
78 | " 'float': 4440629054,\n",
79 | " 'avg10Volume': 54435185.2,\n",
80 | " 'avg30Volume': 39067154.1,\n",
81 | " 'ttmEPS': 13.7084,\n",
82 | " 'ttmDividendRate': 3.22,\n",
83 | " 'companyName': 'Apple, Inc.',\n",
84 | " 'sharesOutstanding': 4331609946,\n",
85 | " 'maxChangePercent': 452.5766,\n",
86 | " 'year5ChangePercent': 3.0546,\n",
87 | " 'year2ChangePercent': 1.1867,\n",
88 | " 'year1ChangePercent': 1.186376,\n",
89 | " 'ytdChangePercent': 0.512578,\n",
90 | " 'month6ChangePercent': 0.407457,\n",
91 | " 'month3ChangePercent': 0.485051,\n",
92 | " 'month1ChangePercent': 0.19254,\n",
93 | " 'day30ChangePercent': 0.253108,\n",
94 | " 'day5ChangePercent': -0.008155,\n",
95 | " 'nextDividendDate': '2020-08-16',\n",
96 | " 'dividendYield': 0.007235663525925464,\n",
97 | " 'nextEarningsDate': '2020-10-17',\n",
98 | " 'exDividendDate': '2020-08-06',\n",
99 | " 'peRatio': 34.17,\n",
100 | " 'beta': 1.15885673879414}"
101 | ]
102 | },
103 | "execution_count": 3,
104 | "metadata": {},
105 | "output_type": "execute_result"
106 | }
107 | ],
108 | "source": [
109 | "symbol = 'AAPL'\n",
110 | "api_url = f'https://sandbox.iexapis.com/stable/stock/{symbol}/stats?token={IEX_CLOUD_API_TOKEN}'\n",
111 | "data = requests.get(api_url).json()\n",
112 | "data"
113 | ]
114 | },
115 | {
116 | "cell_type": "markdown",
117 | "metadata": {},
118 | "source": [
119 | "## Parsing Our API Call\n",
120 | "\n",
121 | "This API call has all the information we need. We can parse it using the same square-bracket notation as in the first project of this course. Here is an example."
122 | ]
123 | },
124 | {
125 | "cell_type": "code",
126 | "execution_count": 4,
127 | "metadata": {},
128 | "outputs": [
129 | {
130 | "data": {
131 | "text/plain": [
132 | "1.186376"
133 | ]
134 | },
135 | "execution_count": 4,
136 | "metadata": {},
137 | "output_type": "execute_result"
138 | }
139 | ],
140 | "source": [
141 | "data['year1ChangePercent']"
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "metadata": {},
147 | "source": [
148 | "## Executing A Batch API Call & Building Our DataFrame\n",
149 | "\n",
150 | "Just like in our first project, it's now time to execute several batch API calls and add the information we need to our DataFrame.\n",
151 | "\n",
152 | "We'll start by running the following code cell, which contains some code we already built last time that we can re-use for this project. More specifically, it contains a function called `chunks` that we can use to divide our list of securities into groups of 100."
153 | ]
154 | },
155 | {
156 | "cell_type": "code",
157 | "execution_count": 5,
158 | "metadata": {},
159 | "outputs": [],
160 | "source": [
161 | "# Function sourced from \n",
162 | "# https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks\n",
163 | "def chunks(lst, n):\n",
164 | " \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n",
165 | " for i in range(0, len(lst), n):\n",
166 | " yield lst[i:i + n] \n",
167 | " \n",
168 | "symbol_groups = list(chunks(stocks['Ticker'], 100))\n",
169 | "symbol_strings = []\n",
170 | "for i in range(0, len(symbol_groups)):\n",
171 | " symbol_strings.append(','.join(symbol_groups[i]))\n",
172 | "# print(symbol_strings[i])\n",
173 | "\n",
174 | "my_columns = ['Ticker', 'Price', 'One-Year Price Return', 'Number of Shares to Buy']"
175 | ]
176 | },
177 | {
178 | "cell_type": "markdown",
179 | "metadata": {},
180 | "source": [
181 | "Now we need to create a blank DataFrame and add our data to the data frame one-by-one."
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": 6,
187 | "metadata": {},
188 | "outputs": [
189 | {
190 | "data": {
191 | "text/html": [
192 | "
\n",
193 | "\n",
206 | "
\n",
207 | " \n",
208 | " \n",
209 | " | \n",
210 | " Ticker | \n",
211 | " Price | \n",
212 | " One-Year Price Return | \n",
213 | " Number of Shares to Buy | \n",
214 | "
\n",
215 | " \n",
216 | " \n",
217 | " \n",
218 | " 0 | \n",
219 | " A | \n",
220 | " 101.50 | \n",
221 | " 0.452986 | \n",
222 | " N/A | \n",
223 | "
\n",
224 | " \n",
225 | " 1 | \n",
226 | " AAL | \n",
227 | " 13.65 | \n",
228 | " -0.527621 | \n",
229 | " N/A | \n",
230 | "
\n",
231 | " \n",
232 | " 2 | \n",
233 | " AAP | \n",
234 | " 157.72 | \n",
235 | " 0.088479 | \n",
236 | " N/A | \n",
237 | "
\n",
238 | " \n",
239 | " 3 | \n",
240 | " AAPL | \n",
241 | " 453.87 | \n",
242 | " 1.172528 | \n",
243 | " N/A | \n",
244 | "
\n",
245 | " \n",
246 | " 4 | \n",
247 | " ABBV | \n",
248 | " 95.71 | \n",
249 | " 0.476493 | \n",
250 | " N/A | \n",
251 | "
\n",
252 | " \n",
253 | " ... | \n",
254 | " ... | \n",
255 | " ... | \n",
256 | " ... | \n",
257 | " ... | \n",
258 | "
\n",
259 | " \n",
260 | " 500 | \n",
261 | " YUM | \n",
262 | " 93.50 | \n",
263 | " -0.208066 | \n",
264 | " N/A | \n",
265 | "
\n",
266 | " \n",
267 | " 501 | \n",
268 | " ZBH | \n",
269 | " 138.91 | \n",
270 | " 0.003031 | \n",
271 | " N/A | \n",
272 | "
\n",
273 | " \n",
274 | " 502 | \n",
275 | " ZBRA | \n",
276 | " 287.51 | \n",
277 | " 0.369427 | \n",
278 | " N/A | \n",
279 | "
\n",
280 | " \n",
281 | " 503 | \n",
282 | " ZION | \n",
283 | " 35.73 | \n",
284 | " -0.162236 | \n",
285 | " N/A | \n",
286 | "
\n",
287 | " \n",
288 | " 504 | \n",
289 | " ZTS | \n",
290 | " 165.46 | \n",
291 | " 0.288770 | \n",
292 | " N/A | \n",
293 | "
\n",
294 | " \n",
295 | "
\n",
296 | "
505 rows × 4 columns
\n",
297 | "
"
298 | ],
299 | "text/plain": [
300 | " Ticker Price One-Year Price Return Number of Shares to Buy\n",
301 | "0 A 101.50 0.452986 N/A\n",
302 | "1 AAL 13.65 -0.527621 N/A\n",
303 | "2 AAP 157.72 0.088479 N/A\n",
304 | "3 AAPL 453.87 1.172528 N/A\n",
305 | "4 ABBV 95.71 0.476493 N/A\n",
306 | ".. ... ... ... ...\n",
307 | "500 YUM 93.50 -0.208066 N/A\n",
308 | "501 ZBH 138.91 0.003031 N/A\n",
309 | "502 ZBRA 287.51 0.369427 N/A\n",
310 | "503 ZION 35.73 -0.162236 N/A\n",
311 | "504 ZTS 165.46 0.288770 N/A\n",
312 | "\n",
313 | "[505 rows x 4 columns]"
314 | ]
315 | },
316 | "execution_count": 6,
317 | "metadata": {},
318 | "output_type": "execute_result"
319 | }
320 | ],
321 | "source": [
322 | "final_dataframe = pd.DataFrame(columns = my_columns)\n",
323 | "\n",
324 | "for symbol_string in symbol_strings:\n",
325 | "# print(symbol_strings)\n",
326 | " batch_api_call_url = f'https://sandbox.iexapis.com/stable/stock/market/batch/?types=stats,quote&symbols={symbol_string}&token={IEX_CLOUD_API_TOKEN}'\n",
327 | " data = requests.get(batch_api_call_url).json()\n",
328 | " for symbol in symbol_string.split(','):\n",
329 | " final_dataframe = final_dataframe.append(\n",
330 | " pd.Series([symbol, \n",
331 | " data[symbol]['quote']['latestPrice'],\n",
332 | " data[symbol]['stats']['year1ChangePercent'],\n",
333 | " 'N/A'\n",
334 | " ], \n",
335 | " index = my_columns), \n",
336 | " ignore_index = True)\n",
337 | " \n",
338 | " \n",
339 | "final_dataframe"
340 | ]
341 | },
342 | {
343 | "cell_type": "markdown",
344 | "metadata": {},
345 | "source": [
346 | "## Removing Low-Momentum Stocks\n",
347 | "\n",
348 | "The investment strategy that we're building seeks to identify the 50 highest-momentum stocks in the S&P 500.\n",
349 | "\n",
350 | "Because of this, the next thing we need to do is remove all the stocks in our DataFrame that fall below this momentum threshold. We'll sort the DataFrame by the stocks' one-year price return, and drop all stocks outside the top 50.\n"
351 | ]
352 | },
353 | {
354 | "cell_type": "code",
355 | "execution_count": 7,
356 | "metadata": {},
357 | "outputs": [
358 | {
359 | "data": {
360 | "text/html": [
361 | "\n",
362 | "\n",
375 | "
\n",
376 | " \n",
377 | " \n",
378 | " | \n",
379 | " Ticker | \n",
380 | " Price | \n",
381 | " One-Year Price Return | \n",
382 | " Number of Shares to Buy | \n",
383 | "
\n",
384 | " \n",
385 | " \n",
386 | " \n",
387 | " 0 | \n",
388 | " NVDA | \n",
389 | " 458.14 | \n",
390 | " 2.015964 | \n",
391 | " N/A | \n",
392 | "
\n",
393 | " \n",
394 | " 1 | \n",
395 | " DXCM | \n",
396 | " 441.29 | \n",
397 | " 1.721219 | \n",
398 | " N/A | \n",
399 | "
\n",
400 | " \n",
401 | " 2 | \n",
402 | " AMD | \n",
403 | " 85.46 | \n",
404 | " 1.632817 | \n",
405 | " N/A | \n",
406 | "
\n",
407 | " \n",
408 | " 3 | \n",
409 | " CARR | \n",
410 | " 29.94 | \n",
411 | " 1.489900 | \n",
412 | " N/A | \n",
413 | "
\n",
414 | " \n",
415 | " 4 | \n",
416 | " AAPL | \n",
417 | " 453.87 | \n",
418 | " 1.172528 | \n",
419 | " N/A | \n",
420 | "
\n",
421 | " \n",
422 | " 5 | \n",
423 | " REGN | \n",
424 | " 624.50 | \n",
425 | " 1.031728 | \n",
426 | " N/A | \n",
427 | "
\n",
428 | " \n",
429 | " 6 | \n",
430 | " SWKS | \n",
431 | " 153.26 | \n",
432 | " 0.921570 | \n",
433 | " N/A | \n",
434 | "
\n",
435 | " \n",
436 | " 7 | \n",
437 | " WST | \n",
438 | " 283.20 | \n",
439 | " 0.921000 | \n",
440 | " N/A | \n",
441 | "
\n",
442 | " \n",
443 | " 8 | \n",
444 | " LRCX | \n",
445 | " 393.68 | \n",
446 | " 0.892740 | \n",
447 | " N/A | \n",
448 | "
\n",
449 | " \n",
450 | " 9 | \n",
451 | " QRVO | \n",
452 | " 132.38 | \n",
453 | " 0.858187 | \n",
454 | " N/A | \n",
455 | "
\n",
456 | " \n",
457 | " 10 | \n",
458 | " PYPL | \n",
459 | " 196.30 | \n",
460 | " 0.829338 | \n",
461 | " N/A | \n",
462 | "
\n",
463 | " \n",
464 | " 11 | \n",
465 | " AMZN | \n",
466 | " 3192.88 | \n",
467 | " 0.759898 | \n",
468 | " N/A | \n",
469 | "
\n",
470 | " \n",
471 | " 12 | \n",
472 | " ATVI | \n",
473 | " 83.82 | \n",
474 | " 0.710587 | \n",
475 | " N/A | \n",
476 | "
\n",
477 | " \n",
478 | " 13 | \n",
479 | " ALGN | \n",
480 | " 313.73 | \n",
481 | " 0.709093 | \n",
482 | " N/A | \n",
483 | "
\n",
484 | " \n",
485 | " 14 | \n",
486 | " NEM | \n",
487 | " 63.72 | \n",
488 | " 0.698936 | \n",
489 | " N/A | \n",
490 | "
\n",
491 | " \n",
492 | " 15 | \n",
493 | " ODFL | \n",
494 | " 196.24 | \n",
495 | " 0.690263 | \n",
496 | " N/A | \n",
497 | "
\n",
498 | " \n",
499 | " 16 | \n",
500 | " ROL | \n",
501 | " 55.90 | \n",
502 | " 0.676072 | \n",
503 | " N/A | \n",
504 | "
\n",
505 | " \n",
506 | " 17 | \n",
507 | " NOW | \n",
508 | " 439.05 | \n",
509 | " 0.656782 | \n",
510 | " N/A | \n",
511 | "
\n",
512 | " \n",
513 | " 18 | \n",
514 | " LOW | \n",
515 | " 159.19 | \n",
516 | " 0.646186 | \n",
517 | " N/A | \n",
518 | "
\n",
519 | " \n",
520 | " 19 | \n",
521 | " DPZ | \n",
522 | " 405.67 | \n",
523 | " 0.644884 | \n",
524 | " N/A | \n",
525 | "
\n",
526 | " \n",
527 | " 20 | \n",
528 | " FBHS | \n",
529 | " 85.93 | \n",
530 | " 0.635603 | \n",
531 | " N/A | \n",
532 | "
\n",
533 | " \n",
534 | " 21 | \n",
535 | " FAST | \n",
536 | " 49.98 | \n",
537 | " 0.624303 | \n",
538 | " N/A | \n",
539 | "
\n",
540 | " \n",
541 | " 22 | \n",
542 | " TGT | \n",
543 | " 136.10 | \n",
544 | " 0.616877 | \n",
545 | " N/A | \n",
546 | "
\n",
547 | " \n",
548 | " 23 | \n",
549 | " QCOM | \n",
550 | " 119.34 | \n",
551 | " 0.609546 | \n",
552 | " N/A | \n",
553 | "
\n",
554 | " \n",
555 | " 24 | \n",
556 | " URI | \n",
557 | " 181.40 | \n",
558 | " 0.590406 | \n",
559 | " N/A | \n",
560 | "
\n",
561 | " \n",
562 | " 25 | \n",
563 | " CHTR | \n",
564 | " 602.51 | \n",
565 | " 0.586074 | \n",
566 | " N/A | \n",
567 | "
\n",
568 | " \n",
569 | " 26 | \n",
570 | " MSCI | \n",
571 | " 369.93 | \n",
572 | " 0.583839 | \n",
573 | " N/A | \n",
574 | "
\n",
575 | " \n",
576 | " 27 | \n",
577 | " VAR | \n",
578 | " 174.79 | \n",
579 | " 0.569642 | \n",
580 | " N/A | \n",
581 | "
\n",
582 | " \n",
583 | " 28 | \n",
584 | " ROK | \n",
585 | " 247.30 | \n",
586 | " 0.560821 | \n",
587 | " N/A | \n",
588 | "
\n",
589 | " \n",
590 | " 29 | \n",
591 | " BIO | \n",
592 | " 517.62 | \n",
593 | " 0.558503 | \n",
594 | " N/A | \n",
595 | "
\n",
596 | " \n",
597 | " 30 | \n",
598 | " KSU | \n",
599 | " 193.03 | \n",
600 | " 0.548382 | \n",
601 | " N/A | \n",
602 | "
\n",
603 | " \n",
604 | " 31 | \n",
605 | " KLAC | \n",
606 | " 216.00 | \n",
607 | " 0.547432 | \n",
608 | " N/A | \n",
609 | "
\n",
610 | " \n",
611 | " 32 | \n",
612 | " ADBE | \n",
613 | " 449.01 | \n",
614 | " 0.540159 | \n",
615 | " N/A | \n",
616 | "
\n",
617 | " \n",
618 | " 33 | \n",
619 | " ABMD | \n",
620 | " 307.07 | \n",
621 | " 0.537076 | \n",
622 | " N/A | \n",
623 | "
\n",
624 | " \n",
625 | " 34 | \n",
626 | " CDNS | \n",
627 | " 111.60 | \n",
628 | " 0.532542 | \n",
629 | " N/A | \n",
630 | "
\n",
631 | " \n",
632 | " 35 | \n",
633 | " NFLX | \n",
634 | " 486.12 | \n",
635 | " 0.531236 | \n",
636 | " N/A | \n",
637 | "
\n",
638 | " \n",
639 | " 36 | \n",
640 | " ADSK | \n",
641 | " 240.90 | \n",
642 | " 0.526803 | \n",
643 | " N/A | \n",
644 | "
\n",
645 | " \n",
646 | " 37 | \n",
647 | " FTNT | \n",
648 | " 132.50 | \n",
649 | " 0.522105 | \n",
650 | " N/A | \n",
651 | "
\n",
652 | " \n",
653 | " 38 | \n",
654 | " MSFT | \n",
655 | " 215.73 | \n",
656 | " 0.518900 | \n",
657 | " N/A | \n",
658 | "
\n",
659 | " \n",
660 | " 39 | \n",
661 | " EA | \n",
662 | " 145.30 | \n",
663 | " 0.517087 | \n",
664 | " N/A | \n",
665 | "
\n",
666 | " \n",
667 | " 40 | \n",
668 | " TMO | \n",
669 | " 422.95 | \n",
670 | " 0.513970 | \n",
671 | " N/A | \n",
672 | "
\n",
673 | " \n",
674 | " 41 | \n",
675 | " KR | \n",
676 | " 36.29 | \n",
677 | " 0.502626 | \n",
678 | " N/A | \n",
679 | "
\n",
680 | " \n",
681 | " 42 | \n",
682 | " DHI | \n",
683 | " 73.64 | \n",
684 | " 0.499570 | \n",
685 | " N/A | \n",
686 | "
\n",
687 | " \n",
688 | " 43 | \n",
689 | " CTXS | \n",
690 | " 141.53 | \n",
691 | " 0.496854 | \n",
692 | " N/A | \n",
693 | "
\n",
694 | " \n",
695 | " 44 | \n",
696 | " LEN | \n",
697 | " 77.72 | \n",
698 | " 0.495581 | \n",
699 | " N/A | \n",
700 | "
\n",
701 | " \n",
702 | " 45 | \n",
703 | " MAS | \n",
704 | " 61.01 | \n",
705 | " 0.493611 | \n",
706 | " N/A | \n",
707 | "
\n",
708 | " \n",
709 | " 46 | \n",
710 | " TMUS | \n",
711 | " 119.48 | \n",
712 | " 0.490167 | \n",
713 | " N/A | \n",
714 | "
\n",
715 | " \n",
716 | " 47 | \n",
717 | " BBY | \n",
718 | " 107.33 | \n",
719 | " 0.486775 | \n",
720 | " N/A | \n",
721 | "
\n",
722 | " \n",
723 | " 48 | \n",
724 | " VRTX | \n",
725 | " 280.26 | \n",
726 | " 0.486553 | \n",
727 | " N/A | \n",
728 | "
\n",
729 | " \n",
730 | " 49 | \n",
731 | " PWR | \n",
732 | " 50.22 | \n",
733 | " 0.482556 | \n",
734 | " N/A | \n",
735 | "
\n",
736 | " \n",
737 | " 50 | \n",
738 | " OTIS | \n",
739 | " 67.90 | \n",
740 | " 0.481000 | \n",
741 | " N/A | \n",
742 | "
\n",
743 | " \n",
744 | "
\n",
745 | "
"
746 | ],
747 | "text/plain": [
748 | " Ticker Price One-Year Price Return Number of Shares to Buy\n",
749 | "0 NVDA 458.14 2.015964 N/A\n",
750 | "1 DXCM 441.29 1.721219 N/A\n",
751 | "2 AMD 85.46 1.632817 N/A\n",
752 | "3 CARR 29.94 1.489900 N/A\n",
753 | "4 AAPL 453.87 1.172528 N/A\n",
754 | "5 REGN 624.50 1.031728 N/A\n",
755 | "6 SWKS 153.26 0.921570 N/A\n",
756 | "7 WST 283.20 0.921000 N/A\n",
757 | "8 LRCX 393.68 0.892740 N/A\n",
758 | "9 QRVO 132.38 0.858187 N/A\n",
759 | "10 PYPL 196.30 0.829338 N/A\n",
760 | "11 AMZN 3192.88 0.759898 N/A\n",
761 | "12 ATVI 83.82 0.710587 N/A\n",
762 | "13 ALGN 313.73 0.709093 N/A\n",
763 | "14 NEM 63.72 0.698936 N/A\n",
764 | "15 ODFL 196.24 0.690263 N/A\n",
765 | "16 ROL 55.90 0.676072 N/A\n",
766 | "17 NOW 439.05 0.656782 N/A\n",
767 | "18 LOW 159.19 0.646186 N/A\n",
768 | "19 DPZ 405.67 0.644884 N/A\n",
769 | "20 FBHS 85.93 0.635603 N/A\n",
770 | "21 FAST 49.98 0.624303 N/A\n",
771 | "22 TGT 136.10 0.616877 N/A\n",
772 | "23 QCOM 119.34 0.609546 N/A\n",
773 | "24 URI 181.40 0.590406 N/A\n",
774 | "25 CHTR 602.51 0.586074 N/A\n",
775 | "26 MSCI 369.93 0.583839 N/A\n",
776 | "27 VAR 174.79 0.569642 N/A\n",
777 | "28 ROK 247.30 0.560821 N/A\n",
778 | "29 BIO 517.62 0.558503 N/A\n",
779 | "30 KSU 193.03 0.548382 N/A\n",
780 | "31 KLAC 216.00 0.547432 N/A\n",
781 | "32 ADBE 449.01 0.540159 N/A\n",
782 | "33 ABMD 307.07 0.537076 N/A\n",
783 | "34 CDNS 111.60 0.532542 N/A\n",
784 | "35 NFLX 486.12 0.531236 N/A\n",
785 | "36 ADSK 240.90 0.526803 N/A\n",
786 | "37 FTNT 132.50 0.522105 N/A\n",
787 | "38 MSFT 215.73 0.518900 N/A\n",
788 | "39 EA 145.30 0.517087 N/A\n",
789 | "40 TMO 422.95 0.513970 N/A\n",
790 | "41 KR 36.29 0.502626 N/A\n",
791 | "42 DHI 73.64 0.499570 N/A\n",
792 | "43 CTXS 141.53 0.496854 N/A\n",
793 | "44 LEN 77.72 0.495581 N/A\n",
794 | "45 MAS 61.01 0.493611 N/A\n",
795 | "46 TMUS 119.48 0.490167 N/A\n",
796 | "47 BBY 107.33 0.486775 N/A\n",
797 | "48 VRTX 280.26 0.486553 N/A\n",
798 | "49 PWR 50.22 0.482556 N/A\n",
799 | "50 OTIS 67.90 0.481000 N/A"
800 | ]
801 | },
802 | "execution_count": 7,
803 | "metadata": {},
804 | "output_type": "execute_result"
805 | }
806 | ],
807 | "source": [
808 | "final_dataframe.sort_values('One-Year Price Return', ascending = False, inplace = True)\n",
809 | "final_dataframe = final_dataframe[:51]\n",
810 | "final_dataframe.reset_index(drop = True, inplace = True)\n",
811 | "final_dataframe"
812 | ]
813 | },
814 | {
815 | "cell_type": "markdown",
816 | "metadata": {},
817 | "source": [
818 | "## Calculating the Number of Shares to Buy\n",
819 | "\n",
820 | "Just like in the last project, we now need to calculate the number of shares we need to buy. The one change we're going to make is wrapping this functionality inside a function, since we'll be using it again later in this Jupyter Notebook.\n",
821 | "\n",
822 | "Since we've already done most of the work on this, try to complete the following two code cells without watching me do it first!"
823 | ]
824 | },
825 | {
826 | "cell_type": "code",
827 | "execution_count": 8,
828 | "metadata": {},
829 | "outputs": [
830 | {
831 | "name": "stdout",
832 | "output_type": "stream",
833 | "text": [
834 | "Enter the value of your portfolio:1000000\n",
835 | "1000000\n"
836 | ]
837 | }
838 | ],
839 | "source": [
840 | "def portfolio_input():\n",
841 | " global portfolio_size\n",
842 | " portfolio_size = input(\"Enter the value of your portfolio:\")\n",
843 | "\n",
844 | " try:\n",
845 | " val = float(portfolio_size)\n",
846 | " except ValueError:\n",
847 | " print(\"That's not a number! \\n Try again:\")\n",
848 | " portfolio_size = input(\"Enter the value of your portfolio:\")\n",
849 | "\n",
850 | "portfolio_input()\n",
851 | "print(portfolio_size)"
852 | ]
853 | },
854 | {
855 | "cell_type": "code",
856 | "execution_count": 9,
857 | "metadata": {},
858 | "outputs": [
859 | {
860 | "name": "stderr",
861 | "output_type": "stream",
862 | "text": [
863 | "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pandas/core/indexing.py:494: SettingWithCopyWarning: \n",
864 | "A value is trying to be set on a copy of a slice from a DataFrame.\n",
865 | "Try using .loc[row_indexer,col_indexer] = value instead\n",
866 | "\n",
867 | "See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
868 | " self.obj[item] = s\n"
869 | ]
870 | },
871 | {
872 | "data": {
873 | "text/html": [
874 | "\n",
875 | "\n",
888 | "
\n",
889 | " \n",
890 | " \n",
891 | " | \n",
892 | " Ticker | \n",
893 | " Price | \n",
894 | " One-Year Price Return | \n",
895 | " Number of Shares to Buy | \n",
896 | "
\n",
897 | " \n",
898 | " \n",
899 | " \n",
900 | " 0 | \n",
901 | " NVDA | \n",
902 | " 458.14 | \n",
903 | " 2.015964 | \n",
904 | " 42 | \n",
905 | "
\n",
906 | " \n",
907 | " 1 | \n",
908 | " DXCM | \n",
909 | " 441.29 | \n",
910 | " 1.721219 | \n",
911 | " 44 | \n",
912 | "
\n",
913 | " \n",
914 | " 2 | \n",
915 | " AMD | \n",
916 | " 85.46 | \n",
917 | " 1.632817 | \n",
918 | " 229 | \n",
919 | "
\n",
920 | " \n",
921 | " 3 | \n",
922 | " CARR | \n",
923 | " 29.94 | \n",
924 | " 1.489900 | \n",
925 | " 654 | \n",
926 | "
\n",
927 | " \n",
928 | " 4 | \n",
929 | " AAPL | \n",
930 | " 453.87 | \n",
931 | " 1.172528 | \n",
932 | " 43 | \n",
933 | "
\n",
934 | " \n",
935 | " 5 | \n",
936 | " REGN | \n",
937 | " 624.50 | \n",
938 | " 1.031728 | \n",
939 | " 31 | \n",
940 | "
\n",
941 | " \n",
942 | " 6 | \n",
943 | " SWKS | \n",
944 | " 153.26 | \n",
945 | " 0.921570 | \n",
946 | " 127 | \n",
947 | "
\n",
948 | " \n",
949 | " 7 | \n",
950 | " WST | \n",
951 | " 283.20 | \n",
952 | " 0.921000 | \n",
953 | " 69 | \n",
954 | "
\n",
955 | " \n",
956 | " 8 | \n",
957 | " LRCX | \n",
958 | " 393.68 | \n",
959 | " 0.892740 | \n",
960 | " 49 | \n",
961 | "
\n",
962 | " \n",
963 | " 9 | \n",
964 | " QRVO | \n",
965 | " 132.38 | \n",
966 | " 0.858187 | \n",
967 | " 148 | \n",
968 | "
\n",
969 | " \n",
970 | " 10 | \n",
971 | " PYPL | \n",
972 | " 196.30 | \n",
973 | " 0.829338 | \n",
974 | " 99 | \n",
975 | "
\n",
976 | " \n",
977 | " 11 | \n",
978 | " AMZN | \n",
979 | " 3192.88 | \n",
980 | " 0.759898 | \n",
981 | " 6 | \n",
982 | "
\n",
983 | " \n",
984 | " 12 | \n",
985 | " ATVI | \n",
986 | " 83.82 | \n",
987 | " 0.710587 | \n",
988 | " 233 | \n",
989 | "
\n",
990 | " \n",
991 | " 13 | \n",
992 | " ALGN | \n",
993 | " 313.73 | \n",
994 | " 0.709093 | \n",
995 | " 62 | \n",
996 | "
\n",
997 | " \n",
998 | " 14 | \n",
999 | " NEM | \n",
1000 | " 63.72 | \n",
1001 | " 0.698936 | \n",
1002 | " 307 | \n",
1003 | "
\n",
1004 | " \n",
1005 | " 15 | \n",
1006 | " ODFL | \n",
1007 | " 196.24 | \n",
1008 | " 0.690263 | \n",
1009 | " 99 | \n",
1010 | "
\n",
1011 | " \n",
1012 | " 16 | \n",
1013 | " ROL | \n",
1014 | " 55.90 | \n",
1015 | " 0.676072 | \n",
1016 | " 350 | \n",
1017 | "
\n",
1018 | " \n",
1019 | " 17 | \n",
1020 | " NOW | \n",
1021 | " 439.05 | \n",
1022 | " 0.656782 | \n",
1023 | " 44 | \n",
1024 | "
\n",
1025 | " \n",
1026 | " 18 | \n",
1027 | " LOW | \n",
1028 | " 159.19 | \n",
1029 | " 0.646186 | \n",
1030 | " 123 | \n",
1031 | "
\n",
1032 | " \n",
1033 | " 19 | \n",
1034 | " DPZ | \n",
1035 | " 405.67 | \n",
1036 | " 0.644884 | \n",
1037 | " 48 | \n",
1038 | "
\n",
1039 | " \n",
1040 | " 20 | \n",
1041 | " FBHS | \n",
1042 | " 85.93 | \n",
1043 | " 0.635603 | \n",
1044 | " 228 | \n",
1045 | "
\n",
1046 | " \n",
1047 | " 21 | \n",
1048 | " FAST | \n",
1049 | " 49.98 | \n",
1050 | " 0.624303 | \n",
1051 | " 392 | \n",
1052 | "
\n",
1053 | " \n",
1054 | " 22 | \n",
1055 | " TGT | \n",
1056 | " 136.10 | \n",
1057 | " 0.616877 | \n",
1058 | " 144 | \n",
1059 | "
\n",
1060 | " \n",
1061 | " 23 | \n",
1062 | " QCOM | \n",
1063 | " 119.34 | \n",
1064 | " 0.609546 | \n",
1065 | " 164 | \n",
1066 | "
\n",
1067 | " \n",
1068 | " 24 | \n",
1069 | " URI | \n",
1070 | " 181.40 | \n",
1071 | " 0.590406 | \n",
1072 | " 108 | \n",
1073 | "
\n",
1074 | " \n",
1075 | " 25 | \n",
1076 | " CHTR | \n",
1077 | " 602.51 | \n",
1078 | " 0.586074 | \n",
1079 | " 32 | \n",
1080 | "
\n",
1081 | " \n",
1082 | " 26 | \n",
1083 | " MSCI | \n",
1084 | " 369.93 | \n",
1085 | " 0.583839 | \n",
1086 | " 53 | \n",
1087 | "
\n",
1088 | " \n",
1089 | " 27 | \n",
1090 | " VAR | \n",
1091 | " 174.79 | \n",
1092 | " 0.569642 | \n",
1093 | " 112 | \n",
1094 | "
\n",
1095 | " \n",
1096 | " 28 | \n",
1097 | " ROK | \n",
1098 | " 247.30 | \n",
1099 | " 0.560821 | \n",
1100 | " 79 | \n",
1101 | "
\n",
1102 | " \n",
1103 | " 29 | \n",
1104 | " BIO | \n",
1105 | " 517.62 | \n",
1106 | " 0.558503 | \n",
1107 | " 37 | \n",
1108 | "
\n",
1109 | " \n",
1110 | " 30 | \n",
1111 | " KSU | \n",
1112 | " 193.03 | \n",
1113 | " 0.548382 | \n",
1114 | " 101 | \n",
1115 | "
\n",
1116 | " \n",
1117 | " 31 | \n",
1118 | " KLAC | \n",
1119 | " 216.00 | \n",
1120 | " 0.547432 | \n",
1121 | " 90 | \n",
1122 | "
\n",
1123 | " \n",
1124 | " 32 | \n",
1125 | " ADBE | \n",
1126 | " 449.01 | \n",
1127 | " 0.540159 | \n",
1128 | " 43 | \n",
1129 | "
\n",
1130 | " \n",
1131 | " 33 | \n",
1132 | " ABMD | \n",
1133 | " 307.07 | \n",
1134 | " 0.537076 | \n",
1135 | " 63 | \n",
1136 | "
\n",
1137 | " \n",
1138 | " 34 | \n",
1139 | " CDNS | \n",
1140 | " 111.60 | \n",
1141 | " 0.532542 | \n",
1142 | " 175 | \n",
1143 | "
\n",
1144 | " \n",
1145 | " 35 | \n",
1146 | " NFLX | \n",
1147 | " 486.12 | \n",
1148 | " 0.531236 | \n",
1149 | " 40 | \n",
1150 | "
\n",
1151 | " \n",
1152 | " 36 | \n",
1153 | " ADSK | \n",
1154 | " 240.90 | \n",
1155 | " 0.526803 | \n",
1156 | " 81 | \n",
1157 | "
\n",
1158 | " \n",
1159 | " 37 | \n",
1160 | " FTNT | \n",
1161 | " 132.50 | \n",
1162 | " 0.522105 | \n",
1163 | " 147 | \n",
1164 | "
\n",
1165 | " \n",
1166 | " 38 | \n",
1167 | " MSFT | \n",
1168 | " 215.73 | \n",
1169 | " 0.518900 | \n",
1170 | " 90 | \n",
1171 | "
\n",
1172 | " \n",
1173 | " 39 | \n",
1174 | " EA | \n",
1175 | " 145.30 | \n",
1176 | " 0.517087 | \n",
1177 | " 134 | \n",
1178 | "
\n",
1179 | " \n",
1180 | " 40 | \n",
1181 | " TMO | \n",
1182 | " 422.95 | \n",
1183 | " 0.513970 | \n",
1184 | " 46 | \n",
1185 | "
\n",
1186 | " \n",
1187 | " 41 | \n",
1188 | " KR | \n",
1189 | " 36.29 | \n",
1190 | " 0.502626 | \n",
1191 | " 540 | \n",
1192 | "
\n",
1193 | " \n",
1194 | " 42 | \n",
1195 | " DHI | \n",
1196 | " 73.64 | \n",
1197 | " 0.499570 | \n",
1198 | " 266 | \n",
1199 | "
\n",
1200 | " \n",
1201 | " 43 | \n",
1202 | " CTXS | \n",
1203 | " 141.53 | \n",
1204 | " 0.496854 | \n",
1205 | " 138 | \n",
1206 | "
\n",
1207 | " \n",
1208 | " 44 | \n",
1209 | " LEN | \n",
1210 | " 77.72 | \n",
1211 | " 0.495581 | \n",
1212 | " 252 | \n",
1213 | "
\n",
1214 | " \n",
1215 | " 45 | \n",
1216 | " MAS | \n",
1217 | " 61.01 | \n",
1218 | " 0.493611 | \n",
1219 | " 321 | \n",
1220 | "
\n",
1221 | " \n",
1222 | " 46 | \n",
1223 | " TMUS | \n",
1224 | " 119.48 | \n",
1225 | " 0.490167 | \n",
1226 | " 164 | \n",
1227 | "
\n",
1228 | " \n",
1229 | " 47 | \n",
1230 | " BBY | \n",
1231 | " 107.33 | \n",
1232 | " 0.486775 | \n",
1233 | " 182 | \n",
1234 | "
\n",
1235 | " \n",
1236 | " 48 | \n",
1237 | " VRTX | \n",
1238 | " 280.26 | \n",
1239 | " 0.486553 | \n",
1240 | " 69 | \n",
1241 | "
\n",
1242 | " \n",
1243 | " 49 | \n",
1244 | " PWR | \n",
1245 | " 50.22 | \n",
1246 | " 0.482556 | \n",
1247 | " 390 | \n",
1248 | "
\n",
1249 | " \n",
1250 | " 50 | \n",
1251 | " OTIS | \n",
1252 | " 67.90 | \n",
1253 | " 0.481000 | \n",
1254 | " 288 | \n",
1255 | "
\n",
1256 | " \n",
1257 | "
\n",
1258 | "
"
1259 | ],
1260 | "text/plain": [
1261 | " Ticker Price One-Year Price Return Number of Shares to Buy\n",
1262 | "0 NVDA 458.14 2.015964 42\n",
1263 | "1 DXCM 441.29 1.721219 44\n",
1264 | "2 AMD 85.46 1.632817 229\n",
1265 | "3 CARR 29.94 1.489900 654\n",
1266 | "4 AAPL 453.87 1.172528 43\n",
1267 | "5 REGN 624.50 1.031728 31\n",
1268 | "6 SWKS 153.26 0.921570 127\n",
1269 | "7 WST 283.20 0.921000 69\n",
1270 | "8 LRCX 393.68 0.892740 49\n",
1271 | "9 QRVO 132.38 0.858187 148\n",
1272 | "10 PYPL 196.30 0.829338 99\n",
1273 | "11 AMZN 3192.88 0.759898 6\n",
1274 | "12 ATVI 83.82 0.710587 233\n",
1275 | "13 ALGN 313.73 0.709093 62\n",
1276 | "14 NEM 63.72 0.698936 307\n",
1277 | "15 ODFL 196.24 0.690263 99\n",
1278 | "16 ROL 55.90 0.676072 350\n",
1279 | "17 NOW 439.05 0.656782 44\n",
1280 | "18 LOW 159.19 0.646186 123\n",
1281 | "19 DPZ 405.67 0.644884 48\n",
1282 | "20 FBHS 85.93 0.635603 228\n",
1283 | "21 FAST 49.98 0.624303 392\n",
1284 | "22 TGT 136.10 0.616877 144\n",
1285 | "23 QCOM 119.34 0.609546 164\n",
1286 | "24 URI 181.40 0.590406 108\n",
1287 | "25 CHTR 602.51 0.586074 32\n",
1288 | "26 MSCI 369.93 0.583839 53\n",
1289 | "27 VAR 174.79 0.569642 112\n",
1290 | "28 ROK 247.30 0.560821 79\n",
1291 | "29 BIO 517.62 0.558503 37\n",
1292 | "30 KSU 193.03 0.548382 101\n",
1293 | "31 KLAC 216.00 0.547432 90\n",
1294 | "32 ADBE 449.01 0.540159 43\n",
1295 | "33 ABMD 307.07 0.537076 63\n",
1296 | "34 CDNS 111.60 0.532542 175\n",
1297 | "35 NFLX 486.12 0.531236 40\n",
1298 | "36 ADSK 240.90 0.526803 81\n",
1299 | "37 FTNT 132.50 0.522105 147\n",
1300 | "38 MSFT 215.73 0.518900 90\n",
1301 | "39 EA 145.30 0.517087 134\n",
1302 | "40 TMO 422.95 0.513970 46\n",
1303 | "41 KR 36.29 0.502626 540\n",
1304 | "42 DHI 73.64 0.499570 266\n",
1305 | "43 CTXS 141.53 0.496854 138\n",
1306 | "44 LEN 77.72 0.495581 252\n",
1307 | "45 MAS 61.01 0.493611 321\n",
1308 | "46 TMUS 119.48 0.490167 164\n",
1309 | "47 BBY 107.33 0.486775 182\n",
1310 | "48 VRTX 280.26 0.486553 69\n",
1311 | "49 PWR 50.22 0.482556 390\n",
1312 | "50 OTIS 67.90 0.481000 288"
1313 | ]
1314 | },
1315 | "execution_count": 9,
1316 | "metadata": {},
1317 | "output_type": "execute_result"
1318 | }
1319 | ],
1320 | "source": [
1321 | "position_size = float(portfolio_size) / len(final_dataframe.index)\n",
1322 | "for i in range(0, len(final_dataframe['Ticker'])):\n",
1323 | " final_dataframe.loc[i, 'Number of Shares to Buy'] = math.floor(position_size / final_dataframe['Price'][i])\n",
1324 | "final_dataframe"
1325 | ]
1326 | },
1327 | {
1328 | "cell_type": "markdown",
1329 | "metadata": {},
1330 | "source": [
1331 | "## Building a Better (and More Realistic) Momentum Strategy\n",
1332 | "\n",
1333 | "Real-world quantitative investment firms differentiate between \"high quality\" and \"low quality\" momentum stocks:\n",
1334 | "\n",
1335 | "* High-quality momentum stocks show \"slow and steady\" outperformance over long periods of time\n",
1336 | "* Low-quality momentum stocks might not show any momentum for a long time, and then surge upwards.\n",
1337 | "\n",
1338 | "The reason why high-quality momentum stocks are preferred is because low-quality momentum can often be cause by short-term news that is unlikely to be repeated in the future (such as an FDA approval for a biotechnology company).\n",
1339 | "\n",
1340 | "To identify high-quality momentum, we're going to build a strategy that selects stocks from the highest percentiles of: \n",
1341 | "\n",
1342 | "* 1-month price returns\n",
1343 | "* 3-month price returns\n",
1344 | "* 6-month price returns\n",
1345 | "* 1-year price returns\n",
1346 | "\n",
1347 | "Let's start by building our DataFrame. You'll notice that I use the abbreviation `hqm` often. It stands for `high-quality momentum`."
1348 | ]
1349 | },
1350 | {
1351 | "cell_type": "code",
1352 | "execution_count": 10,
1353 | "metadata": {},
1354 | "outputs": [
1355 | {
1356 | "data": {
1357 | "text/plain": [
1358 | "Index(['Ticker', 'Price', 'Number of Shares to Buy', 'One-Year Price Return',\n",
1359 | " 'One-Year Return Percentile', 'Six-Month Price Return',\n",
1360 | " 'Six-Month Return Percentile', 'Three-Month Price Return',\n",
1361 | " 'Three-Month Return Percentile', 'One-Month Price Return',\n",
1362 | " 'One-Month Return Percentile', 'HQM Score'],\n",
1363 | " dtype='object')"
1364 | ]
1365 | },
1366 | "execution_count": 10,
1367 | "metadata": {},
1368 | "output_type": "execute_result"
1369 | }
1370 | ],
1371 | "source": [
1372 | "hqm_columns = [\n",
1373 | " 'Ticker', \n",
1374 | " 'Price', \n",
1375 | " 'Number of Shares to Buy', \n",
1376 | " 'One-Year Price Return', \n",
1377 | " 'One-Year Return Percentile',\n",
1378 | " 'Six-Month Price Return',\n",
1379 | " 'Six-Month Return Percentile',\n",
1380 | " 'Three-Month Price Return',\n",
1381 | " 'Three-Month Return Percentile',\n",
1382 | " 'One-Month Price Return',\n",
1383 | " 'One-Month Return Percentile',\n",
1384 | " 'HQM Score'\n",
1385 | " ]\n",
1386 | "\n",
1387 | "hqm_dataframe = pd.DataFrame(columns = hqm_columns)\n",
1388 | "\n",
1389 | "for symbol_string in symbol_strings:\n",
1390 | "# print(symbol_strings)\n",
1391 | " batch_api_call_url = f'https://sandbox.iexapis.com/stable/stock/market/batch/?types=stats,quote&symbols={symbol_string}&token={IEX_CLOUD_API_TOKEN}'\n",
1392 | " data = requests.get(batch_api_call_url).json()\n",
1393 | " for symbol in symbol_string.split(','):\n",
1394 | " hqm_dataframe = hqm_dataframe.append(\n",
1395 | " pd.Series([symbol, \n",
1396 | " data[symbol]['quote']['latestPrice'],\n",
1397 | " 'N/A',\n",
1398 | " data[symbol]['stats']['year1ChangePercent'],\n",
1399 | " 'N/A',\n",
1400 | " data[symbol]['stats']['month6ChangePercent'],\n",
1401 | " 'N/A',\n",
1402 | " data[symbol]['stats']['month3ChangePercent'],\n",
1403 | " 'N/A',\n",
1404 | " data[symbol]['stats']['month1ChangePercent'],\n",
1405 | " 'N/A',\n",
1406 | " 'N/A'\n",
1407 | " ], \n",
1408 | " index = hqm_columns), \n",
1409 | " ignore_index = True)\n",
1410 | " \n",
1411 | "hqm_dataframe.columns"
1412 | ]
1413 | },
1414 | {
1415 | "cell_type": "markdown",
1416 | "metadata": {},
1417 | "source": [
1418 | "## Calculating Momentum Percentiles\n",
1419 | "\n",
1420 | "We now need to calculate momentum percentile scores for every stock in the universe. More specifically, we need to calculate percentile scores for the following metrics for every stock:\n",
1421 | "\n",
1422 | "* `One-Year Price Return`\n",
1423 | "* `Six-Month Price Return`\n",
1424 | "* `Three-Month Price Return`\n",
1425 | "* `One-Month Price Return`\n",
1426 | "\n",
1427 | "Here's how we'll do this:"
1428 | ]
1429 | },
1430 | {
1431 | "cell_type": "code",
1432 | "execution_count": 11,
1433 | "metadata": {},
1434 | "outputs": [
1435 | {
1436 | "name": "stdout",
1437 | "output_type": "stream",
1438 | "text": [
1439 | "0 0.885149\n",
1440 | "1 0.0237624\n",
1441 | "2 0.578218\n",
1442 | "3 0.992079\n",
1443 | "4 0.89703\n",
1444 | " ... \n",
1445 | "500 0.211881\n",
1446 | "501 0.457426\n",
1447 | "502 0.843564\n",
1448 | "503 0.255446\n",
1449 | "504 0.772277\n",
1450 | "Name: One-Year Return Percentile, Length: 505, dtype: object\n",
1451 | "0 0.837624\n",
1452 | "1 0.0158416\n",
1453 | "2 0.839604\n",
1454 | "3 0.968317\n",
1455 | "4 0.629703\n",
1456 | " ... \n",
1457 | "500 0.405941\n",
1458 | "501 0.39604\n",
1459 | "502 0.906931\n",
1460 | "503 0.227723\n",
1461 | "504 0.776238\n",
1462 | "Name: Six-Month Return Percentile, Length: 505, dtype: object\n",
1463 | "0 0.473267\n",
1464 | "1 0.908911\n",
1465 | "2 0.643564\n",
1466 | "3 0.887129\n",
1467 | "4 0.19802\n",
1468 | " ... \n",
1469 | "500 0.374257\n",
1470 | "501 0.544554\n",
1471 | "502 0.611881\n",
1472 | "503 0.70297\n",
1473 | "504 0.665347\n",
1474 | "Name: Three-Month Return Percentile, Length: 505, dtype: object\n",
1475 | "0 0.530693\n",
1476 | "1 0.827723\n",
1477 | "2 0.742574\n",
1478 | "3 0.879208\n",
1479 | "4 0.0693069\n",
1480 | " ... \n",
1481 | "500 0.370297\n",
1482 | "501 0.762376\n",
1483 | "502 0.641584\n",
1484 | "503 0.312871\n",
1485 | "504 0.792079\n",
1486 | "Name: One-Month Return Percentile, Length: 505, dtype: object\n"
1487 | ]
1488 | },
1489 | {
1490 | "data": {
1491 | "text/html": [
1492 | "\n",
1493 | "\n",
1506 | "
\n",
1507 | " \n",
1508 | " \n",
1509 | " | \n",
1510 | " Ticker | \n",
1511 | " Price | \n",
1512 | " Number of Shares to Buy | \n",
1513 | " One-Year Price Return | \n",
1514 | " One-Year Return Percentile | \n",
1515 | " Six-Month Price Return | \n",
1516 | " Six-Month Return Percentile | \n",
1517 | " Three-Month Price Return | \n",
1518 | " Three-Month Return Percentile | \n",
1519 | " One-Month Price Return | \n",
1520 | " One-Month Return Percentile | \n",
1521 | " HQM Score | \n",
1522 | "
\n",
1523 | " \n",
1524 | " \n",
1525 | " \n",
1526 | " 0 | \n",
1527 | " A | \n",
1528 | " 98.19 | \n",
1529 | " N/A | \n",
1530 | " 0.444090 | \n",
1531 | " 0.885149 | \n",
1532 | " 0.147456 | \n",
1533 | " 0.837624 | \n",
1534 | " 0.221461 | \n",
1535 | " 0.473267 | \n",
1536 | " 0.093820 | \n",
1537 | " 0.530693 | \n",
1538 | " N/A | \n",
1539 | "
\n",
1540 | " \n",
1541 | " 1 | \n",
1542 | " AAL | \n",
1543 | " 13.89 | \n",
1544 | " N/A | \n",
1545 | " -0.526494 | \n",
1546 | " 0.0237624 | \n",
1547 | " -0.564540 | \n",
1548 | " 0.0158416 | \n",
1549 | " 0.509431 | \n",
1550 | " 0.908911 | \n",
1551 | " 0.172430 | \n",
1552 | " 0.827723 | \n",
1553 | " N/A | \n",
1554 | "
\n",
1555 | " \n",
1556 | " 2 | \n",
1557 | " AAP | \n",
1558 | " 161.13 | \n",
1559 | " N/A | \n",
1560 | " 0.088066 | \n",
1561 | " 0.578218 | \n",
1562 | " 0.148378 | \n",
1563 | " 0.839604 | \n",
1564 | " 0.295700 | \n",
1565 | " 0.643564 | \n",
1566 | " 0.144608 | \n",
1567 | " 0.742574 | \n",
1568 | " N/A | \n",
1569 | "
\n",
1570 | " \n",
1571 | " 3 | \n",
1572 | " AAPL | \n",
1573 | " 467.65 | \n",
1574 | " N/A | \n",
1575 | " 1.171724 | \n",
1576 | " 0.992079 | \n",
1577 | " 0.401695 | \n",
1578 | " 0.968317 | \n",
1579 | " 0.474900 | \n",
1580 | " 0.887129 | \n",
1581 | " 0.189840 | \n",
1582 | " 0.879208 | \n",
1583 | " N/A | \n",
1584 | "
\n",
1585 | " \n",
1586 | " 4 | \n",
1587 | " ABBV | \n",
1588 | " 97.29 | \n",
1589 | " N/A | \n",
1590 | " 0.478770 | \n",
1591 | " 0.89703 | \n",
1592 | " 0.001711 | \n",
1593 | " 0.629703 | \n",
1594 | " 0.077880 | \n",
1595 | " 0.19802 | \n",
1596 | " -0.024533 | \n",
1597 | " 0.0693069 | \n",
1598 | " N/A | \n",
1599 | "
\n",
1600 | " \n",
1601 | " ... | \n",
1602 | " ... | \n",
1603 | " ... | \n",
1604 | " ... | \n",
1605 | " ... | \n",
1606 | " ... | \n",
1607 | " ... | \n",
1608 | " ... | \n",
1609 | " ... | \n",
1610 | " ... | \n",
1611 | " ... | \n",
1612 | " ... | \n",
1613 | " ... | \n",
1614 | "
\n",
1615 | " \n",
1616 | " 500 | \n",
1617 | " YUM | \n",
1618 | " 93.51 | \n",
1619 | " N/A | \n",
1620 | " -0.214524 | \n",
1621 | " 0.211881 | \n",
1622 | " -0.116757 | \n",
1623 | " 0.405941 | \n",
1624 | " 0.161550 | \n",
1625 | " 0.374257 | \n",
1626 | " 0.068180 | \n",
1627 | " 0.370297 | \n",
1628 | " N/A | \n",
1629 | "
\n",
1630 | " \n",
1631 | " 501 | \n",
1632 | " ZBH | \n",
1633 | " 140.16 | \n",
1634 | " N/A | \n",
1635 | " 0.003007 | \n",
1636 | " 0.457426 | \n",
1637 | " -0.127491 | \n",
1638 | " 0.39604 | \n",
1639 | " 0.250906 | \n",
1640 | " 0.544554 | \n",
1641 | " 0.151414 | \n",
1642 | " 0.762376 | \n",
1643 | " N/A | \n",
1644 | "
\n",
1645 | " \n",
1646 | " 502 | \n",
1647 | " ZBRA | \n",
1648 | " 285.97 | \n",
1649 | " N/A | \n",
1650 | " 0.373952 | \n",
1651 | " 0.843564 | \n",
1652 | " 0.223856 | \n",
1653 | " 0.906931 | \n",
1654 | " 0.283668 | \n",
1655 | " 0.611881 | \n",
1656 | " 0.115379 | \n",
1657 | " 0.641584 | \n",
1658 | " N/A | \n",
1659 | "
\n",
1660 | " \n",
1661 | " 503 | \n",
1662 | " ZION | \n",
1663 | " 34.95 | \n",
1664 | " N/A | \n",
1665 | " -0.161814 | \n",
1666 | " 0.255446 | \n",
1667 | " -0.255398 | \n",
1668 | " 0.227723 | \n",
1669 | " 0.328190 | \n",
1670 | " 0.70297 | \n",
1671 | " 0.055265 | \n",
1672 | " 0.312871 | \n",
1673 | " N/A | \n",
1674 | "
\n",
1675 | " \n",
1676 | " 504 | \n",
1677 | " ZTS | \n",
1678 | " 163.66 | \n",
1679 | " N/A | \n",
1680 | " 0.290969 | \n",
1681 | " 0.772277 | \n",
1682 | " 0.102747 | \n",
1683 | " 0.776238 | \n",
1684 | " 0.307237 | \n",
1685 | " 0.665347 | \n",
1686 | " 0.157549 | \n",
1687 | " 0.792079 | \n",
1688 | " N/A | \n",
1689 | "
\n",
1690 | " \n",
1691 | "
\n",
1692 | "
505 rows × 12 columns
\n",
1693 | "
"
1694 | ],
1695 | "text/plain": [
1696 | " Ticker Price Number of Shares to Buy One-Year Price Return \\\n",
1697 | "0 A 98.19 N/A 0.444090 \n",
1698 | "1 AAL 13.89 N/A -0.526494 \n",
1699 | "2 AAP 161.13 N/A 0.088066 \n",
1700 | "3 AAPL 467.65 N/A 1.171724 \n",
1701 | "4 ABBV 97.29 N/A 0.478770 \n",
1702 | ".. ... ... ... ... \n",
1703 | "500 YUM 93.51 N/A -0.214524 \n",
1704 | "501 ZBH 140.16 N/A 0.003007 \n",
1705 | "502 ZBRA 285.97 N/A 0.373952 \n",
1706 | "503 ZION 34.95 N/A -0.161814 \n",
1707 | "504 ZTS 163.66 N/A 0.290969 \n",
1708 | "\n",
1709 | " One-Year Return Percentile Six-Month Price Return \\\n",
1710 | "0 0.885149 0.147456 \n",
1711 | "1 0.0237624 -0.564540 \n",
1712 | "2 0.578218 0.148378 \n",
1713 | "3 0.992079 0.401695 \n",
1714 | "4 0.89703 0.001711 \n",
1715 | ".. ... ... \n",
1716 | "500 0.211881 -0.116757 \n",
1717 | "501 0.457426 -0.127491 \n",
1718 | "502 0.843564 0.223856 \n",
1719 | "503 0.255446 -0.255398 \n",
1720 | "504 0.772277 0.102747 \n",
1721 | "\n",
1722 | " Six-Month Return Percentile Three-Month Price Return \\\n",
1723 | "0 0.837624 0.221461 \n",
1724 | "1 0.0158416 0.509431 \n",
1725 | "2 0.839604 0.295700 \n",
1726 | "3 0.968317 0.474900 \n",
1727 | "4 0.629703 0.077880 \n",
1728 | ".. ... ... \n",
1729 | "500 0.405941 0.161550 \n",
1730 | "501 0.39604 0.250906 \n",
1731 | "502 0.906931 0.283668 \n",
1732 | "503 0.227723 0.328190 \n",
1733 | "504 0.776238 0.307237 \n",
1734 | "\n",
1735 | " Three-Month Return Percentile One-Month Price Return \\\n",
1736 | "0 0.473267 0.093820 \n",
1737 | "1 0.908911 0.172430 \n",
1738 | "2 0.643564 0.144608 \n",
1739 | "3 0.887129 0.189840 \n",
1740 | "4 0.19802 -0.024533 \n",
1741 | ".. ... ... \n",
1742 | "500 0.374257 0.068180 \n",
1743 | "501 0.544554 0.151414 \n",
1744 | "502 0.611881 0.115379 \n",
1745 | "503 0.70297 0.055265 \n",
1746 | "504 0.665347 0.157549 \n",
1747 | "\n",
1748 | " One-Month Return Percentile HQM Score \n",
1749 | "0 0.530693 N/A \n",
1750 | "1 0.827723 N/A \n",
1751 | "2 0.742574 N/A \n",
1752 | "3 0.879208 N/A \n",
1753 | "4 0.0693069 N/A \n",
1754 | ".. ... ... \n",
1755 | "500 0.370297 N/A \n",
1756 | "501 0.762376 N/A \n",
1757 | "502 0.641584 N/A \n",
1758 | "503 0.312871 N/A \n",
1759 | "504 0.792079 N/A \n",
1760 | "\n",
1761 | "[505 rows x 12 columns]"
1762 | ]
1763 | },
1764 | "execution_count": 11,
1765 | "metadata": {},
1766 | "output_type": "execute_result"
1767 | }
1768 | ],
1769 | "source": [
1770 | "time_periods = [\n",
1771 | " 'One-Year',\n",
1772 | " 'Six-Month',\n",
1773 | " 'Three-Month',\n",
1774 | " 'One-Month'\n",
1775 | " ]\n",
1776 | "\n",
1777 | "for row in hqm_dataframe.index:\n",
1778 | " for time_period in time_periods:\n",
1779 | " hqm_dataframe.loc[row, f'{time_period} Return Percentile'] = stats.percentileofscore(hqm_dataframe[f'{time_period} Price Return'], hqm_dataframe.loc[row, f'{time_period} Price Return'])/100\n",
1780 | "\n",
1781 | "# Print each percentile score to make sure it was calculated properly\n",
1782 | "for time_period in time_periods:\n",
1783 | " print(hqm_dataframe[f'{time_period} Return Percentile'])\n",
1784 | "\n",
1785 | "#Print the entire DataFrame \n",
1786 | "hqm_dataframe"
1787 | ]
1788 | },
1789 | {
1790 | "cell_type": "markdown",
1791 | "metadata": {},
1792 | "source": [
1793 | "## Calculating the HQM Score\n",
1794 | "\n",
1795 | "We'll now calculate our `HQM Score`, which is the high-quality momentum score that we'll use to filter for stocks in this investing strategy.\n",
1796 | "\n",
1797 | "The `HQM Score` will be the arithmetic mean of the 4 momentum percentile scores that we calculated in the last section.\n",
1798 | "\n",
1799 | "To calculate arithmetic mean, we will use the `mean` function from Python's built-in `statistics` module."
1800 | ]
1801 | },
1802 | {
1803 | "cell_type": "code",
1804 | "execution_count": 12,
1805 | "metadata": {},
1806 | "outputs": [],
1807 | "source": [
1808 | "from statistics import mean\n",
1809 | "\n",
1810 | "for row in hqm_dataframe.index:\n",
1811 | " momentum_percentiles = []\n",
1812 | " for time_period in time_periods:\n",
1813 | " momentum_percentiles.append(hqm_dataframe.loc[row, f'{time_period} Return Percentile'])\n",
1814 | " hqm_dataframe.loc[row, 'HQM Score'] = mean(momentum_percentiles)"
1815 | ]
1816 | },
1817 | {
1818 | "cell_type": "markdown",
1819 | "metadata": {},
1820 | "source": [
1821 | "## Selecting the 50 Best Momentum Stocks\n",
1822 | "\n",
1823 | "As before, we can identify the 50 best momentum stocks in our universe by sorting the DataFrame on the `HQM Score` column and dropping all but the top 50 entries."
1824 | ]
1825 | },
1826 | {
1827 | "cell_type": "code",
1828 | "execution_count": 13,
1829 | "metadata": {},
1830 | "outputs": [],
1831 | "source": [
1832 | "hqm_dataframe.sort_values(by = 'HQM Score', ascending = False)\n",
1833 | "hqm_dataframe = hqm_dataframe[:51]"
1834 | ]
1835 | },
1836 | {
1837 | "cell_type": "markdown",
1838 | "metadata": {},
1839 | "source": [
1840 | "## Calculating the Number of Shares to Buy\n",
1841 | "\n",
1842 | "We'll use the `portfolio_input` function that we created earlier to accept our portfolio size. Then we will use similar logic in a `for` loop to calculate the number of shares to buy for each stock in our investment universe."
1843 | ]
1844 | },
1845 | {
1846 | "cell_type": "code",
1847 | "execution_count": 14,
1848 | "metadata": {},
1849 | "outputs": [
1850 | {
1851 | "name": "stdout",
1852 | "output_type": "stream",
1853 | "text": [
1854 | "Enter the value of your portfolio:1000000\n"
1855 | ]
1856 | }
1857 | ],
1858 | "source": [
1859 | "portfolio_input()"
1860 | ]
1861 | },
1862 | {
1863 | "cell_type": "code",
1864 | "execution_count": 15,
1865 | "metadata": {},
1866 | "outputs": [
1867 | {
1868 | "data": {
1869 | "text/html": [
1870 | "\n",
1871 | "\n",
1884 | "
\n",
1885 | " \n",
1886 | " \n",
1887 | " | \n",
1888 | " Ticker | \n",
1889 | " Price | \n",
1890 | " Number of Shares to Buy | \n",
1891 | " One-Year Price Return | \n",
1892 | " One-Year Return Percentile | \n",
1893 | " Six-Month Price Return | \n",
1894 | " Six-Month Return Percentile | \n",
1895 | " Three-Month Price Return | \n",
1896 | " Three-Month Return Percentile | \n",
1897 | " One-Month Price Return | \n",
1898 | " One-Month Return Percentile | \n",
1899 | " HQM Score | \n",
1900 | "
\n",
1901 | " \n",
1902 | " \n",
1903 | " \n",
1904 | " 0 | \n",
1905 | " A | \n",
1906 | " 98.19 | \n",
1907 | " 199 | \n",
1908 | " 0.444090 | \n",
1909 | " 0.885149 | \n",
1910 | " 0.147456 | \n",
1911 | " 0.837624 | \n",
1912 | " 0.221461 | \n",
1913 | " 0.473267 | \n",
1914 | " 0.093820 | \n",
1915 | " 0.530693 | \n",
1916 | " 0.681683 | \n",
1917 | "
\n",
1918 | " \n",
1919 | " 1 | \n",
1920 | " AAL | \n",
1921 | " 13.89 | \n",
1922 | " 1411 | \n",
1923 | " -0.526494 | \n",
1924 | " 0.0237624 | \n",
1925 | " -0.564540 | \n",
1926 | " 0.0158416 | \n",
1927 | " 0.509431 | \n",
1928 | " 0.908911 | \n",
1929 | " 0.172430 | \n",
1930 | " 0.827723 | \n",
1931 | " 0.444059 | \n",
1932 | "
\n",
1933 | " \n",
1934 | " 2 | \n",
1935 | " AAP | \n",
1936 | " 161.13 | \n",
1937 | " 121 | \n",
1938 | " 0.088066 | \n",
1939 | " 0.578218 | \n",
1940 | " 0.148378 | \n",
1941 | " 0.839604 | \n",
1942 | " 0.295700 | \n",
1943 | " 0.643564 | \n",
1944 | " 0.144608 | \n",
1945 | " 0.742574 | \n",
1946 | " 0.70099 | \n",
1947 | "
\n",
1948 | " \n",
1949 | " 3 | \n",
1950 | " AAPL | \n",
1951 | " 467.65 | \n",
1952 | " 41 | \n",
1953 | " 1.171724 | \n",
1954 | " 0.992079 | \n",
1955 | " 0.401695 | \n",
1956 | " 0.968317 | \n",
1957 | " 0.474900 | \n",
1958 | " 0.887129 | \n",
1959 | " 0.189840 | \n",
1960 | " 0.879208 | \n",
1961 | " 0.931683 | \n",
1962 | "
\n",
1963 | " \n",
1964 | " 4 | \n",
1965 | " ABBV | \n",
1966 | " 97.29 | \n",
1967 | " 201 | \n",
1968 | " 0.478770 | \n",
1969 | " 0.89703 | \n",
1970 | " 0.001711 | \n",
1971 | " 0.629703 | \n",
1972 | " 0.077880 | \n",
1973 | " 0.19802 | \n",
1974 | " -0.024533 | \n",
1975 | " 0.0693069 | \n",
1976 | " 0.448515 | \n",
1977 | "
\n",
1978 | " \n",
1979 | " 5 | \n",
1980 | " ABC | \n",
1981 | " 104.97 | \n",
1982 | " 186 | \n",
1983 | " 0.163705 | \n",
1984 | " 0.651485 | \n",
1985 | " 0.100112 | \n",
1986 | " 0.774257 | \n",
1987 | " 0.241867 | \n",
1988 | " 0.522772 | \n",
1989 | " 0.066990 | \n",
1990 | " 0.364356 | \n",
1991 | " 0.578218 | \n",
1992 | "
\n",
1993 | " \n",
1994 | " 6 | \n",
1995 | " ABMD | \n",
1996 | " 305.78 | \n",
1997 | " 64 | \n",
1998 | " 0.546138 | \n",
1999 | " 0.942574 | \n",
2000 | " 0.874783 | \n",
2001 | " 0.99802 | \n",
2002 | " 0.645795 | \n",
2003 | " 0.954455 | \n",
2004 | " 0.145153 | \n",
2005 | " 0.746535 | \n",
2006 | " 0.910396 | \n",
2007 | "
\n",
2008 | " \n",
2009 | " 7 | \n",
2010 | " ABT | \n",
2011 | " 102.94 | \n",
2012 | " 190 | \n",
2013 | " 0.161073 | \n",
2014 | " 0.647525 | \n",
2015 | " 0.139130 | \n",
2016 | " 0.823762 | \n",
2017 | " 0.093917 | \n",
2018 | " 0.233663 | \n",
2019 | " 0.081815 | \n",
2020 | " 0.457426 | \n",
2021 | " 0.540594 | \n",
2022 | "
\n",
2023 | " \n",
2024 | " 8 | \n",
2025 | " ACN | \n",
2026 | " 237.88 | \n",
2027 | " 82 | \n",
2028 | " 0.197484 | \n",
2029 | " 0.69505 | \n",
2030 | " 0.085519 | \n",
2031 | " 0.762376 | \n",
2032 | " 0.279570 | \n",
2033 | " 0.605941 | \n",
2034 | " 0.067045 | \n",
2035 | " 0.366337 | \n",
2036 | " 0.607426 | \n",
2037 | "
\n",
2038 | " \n",
2039 | " 9 | \n",
2040 | " ADBE | \n",
2041 | " 453.91 | \n",
2042 | " 43 | \n",
2043 | " 0.532839 | \n",
2044 | " 0.934653 | \n",
2045 | " 0.196705 | \n",
2046 | " 0.879208 | \n",
2047 | " 0.252382 | \n",
2048 | " 0.550495 | \n",
2049 | " 0.006829 | \n",
2050 | " 0.150495 | \n",
2051 | " 0.628713 | \n",
2052 | "
\n",
2053 | " \n",
2054 | " 10 | \n",
2055 | " ADI | \n",
2056 | " 120.52 | \n",
2057 | " 162 | \n",
2058 | " 0.056525 | \n",
2059 | " 0.522772 | \n",
2060 | " 0.002920 | \n",
2061 | " 0.631683 | \n",
2062 | " 0.149173 | \n",
2063 | " 0.360396 | \n",
2064 | " 0.017244 | \n",
2065 | " 0.174257 | \n",
2066 | " 0.422277 | \n",
2067 | "
\n",
2068 | " \n",
2069 | " 11 | \n",
2070 | " ADM | \n",
2071 | " 46.62 | \n",
2072 | " 420 | \n",
2073 | " 0.178916 | \n",
2074 | " 0.679208 | \n",
2075 | " -0.018869 | \n",
2076 | " 0.580198 | \n",
2077 | " 0.328413 | \n",
2078 | " 0.70495 | \n",
2079 | " 0.123837 | \n",
2080 | " 0.687129 | \n",
2081 | " 0.662871 | \n",
2082 | "
\n",
2083 | " \n",
2084 | " 12 | \n",
2085 | " ADP | \n",
2086 | " 139.82 | \n",
2087 | " 140 | \n",
2088 | " -0.174896 | \n",
2089 | " 0.243564 | \n",
2090 | " -0.227657 | \n",
2091 | " 0.265347 | \n",
2092 | " 0.037317 | \n",
2093 | " 0.134653 | \n",
2094 | " -0.042030 | \n",
2095 | " 0.0435644 | \n",
2096 | " 0.171782 | \n",
2097 | "
\n",
2098 | " \n",
2099 | " 13 | \n",
2100 | " ADSK | \n",
2101 | " 234.20 | \n",
2102 | " 83 | \n",
2103 | " 0.528696 | \n",
2104 | " 0.928713 | \n",
2105 | " 0.116990 | \n",
2106 | " 0.794059 | \n",
2107 | " 0.312257 | \n",
2108 | " 0.677228 | \n",
2109 | " -0.001259 | \n",
2110 | " 0.122772 | \n",
2111 | " 0.630693 | \n",
2112 | "
\n",
2113 | " \n",
2114 | " 14 | \n",
2115 | " AEE | \n",
2116 | " 85.23 | \n",
2117 | " 230 | \n",
2118 | " 0.073499 | \n",
2119 | " 0.556436 | \n",
2120 | " -0.044706 | \n",
2121 | " 0.538614 | \n",
2122 | " 0.202938 | \n",
2123 | " 0.447525 | \n",
2124 | " 0.086675 | \n",
2125 | " 0.483168 | \n",
2126 | " 0.506436 | \n",
2127 | "
\n",
2128 | " \n",
2129 | " 15 | \n",
2130 | " AEP | \n",
2131 | " 87.88 | \n",
2132 | " 223 | \n",
2133 | " -0.069760 | \n",
2134 | " 0.368317 | \n",
2135 | " -0.186159 | \n",
2136 | " 0.326733 | \n",
2137 | " 0.084255 | \n",
2138 | " 0.217822 | \n",
2139 | " -0.007456 | \n",
2140 | " 0.106931 | \n",
2141 | " 0.25495 | \n",
2142 | "
\n",
2143 | " \n",
2144 | " 16 | \n",
2145 | " AES | \n",
2146 | " 17.54 | \n",
2147 | " 1117 | \n",
2148 | " 0.141966 | \n",
2149 | " 0.635644 | \n",
2150 | " -0.177281 | \n",
2151 | " 0.336634 | \n",
2152 | " 0.510037 | \n",
2153 | " 0.910891 | \n",
2154 | " 0.186968 | \n",
2155 | " 0.873267 | \n",
2156 | " 0.689109 | \n",
2157 | "
\n",
2158 | " \n",
2159 | " 17 | \n",
2160 | " AFL | \n",
2161 | " 39.27 | \n",
2162 | " 499 | \n",
2163 | " -0.298263 | \n",
2164 | " 0.116832 | \n",
2165 | " -0.283044 | \n",
2166 | " 0.190099 | \n",
2167 | " 0.178662 | \n",
2168 | " 0.4 | \n",
2169 | " 0.082055 | \n",
2170 | " 0.459406 | \n",
2171 | " 0.291584 | \n",
2172 | "
\n",
2173 | " \n",
2174 | " 18 | \n",
2175 | " AIG | \n",
2176 | " 31.76 | \n",
2177 | " 617 | \n",
2178 | " -0.468456 | \n",
2179 | " 0.0356436 | \n",
2180 | " -0.398200 | \n",
2181 | " 0.0673267 | \n",
2182 | " 0.242767 | \n",
2183 | " 0.528713 | \n",
2184 | " 0.042150 | \n",
2185 | " 0.271287 | \n",
2186 | " 0.225743 | \n",
2187 | "
\n",
2188 | " \n",
2189 | " 19 | \n",
2190 | " AIV | \n",
2191 | " 39.00 | \n",
2192 | " 502 | \n",
2193 | " -0.260956 | \n",
2194 | " 0.168317 | \n",
2195 | " -0.323003 | \n",
2196 | " 0.142574 | \n",
2197 | " 0.096556 | \n",
2198 | " 0.243564 | \n",
2199 | " 0.004109 | \n",
2200 | " 0.140594 | \n",
2201 | " 0.173762 | \n",
2202 | "
\n",
2203 | " \n",
2204 | " 20 | \n",
2205 | " AIZ | \n",
2206 | " 129.93 | \n",
2207 | " 150 | \n",
2208 | " 0.016575 | \n",
2209 | " 0.475248 | \n",
2210 | " -0.116835 | \n",
2211 | " 0.40396 | \n",
2212 | " 0.418900 | \n",
2213 | " 0.839604 | \n",
2214 | " 0.254327 | \n",
2215 | " 0.948515 | \n",
2216 | " 0.666832 | \n",
2217 | "
\n",
2218 | " \n",
2219 | " 21 | \n",
2220 | " AJG | \n",
2221 | " 111.00 | \n",
2222 | " 176 | \n",
2223 | " 0.195680 | \n",
2224 | " 0.689109 | \n",
2225 | " -0.011750 | \n",
2226 | " 0.6 | \n",
2227 | " 0.253483 | \n",
2228 | " 0.554455 | \n",
2229 | " 0.092902 | \n",
2230 | " 0.520792 | \n",
2231 | " 0.591089 | \n",
2232 | "
\n",
2233 | " \n",
2234 | " 22 | \n",
2235 | " AKAM | \n",
2236 | " 110.00 | \n",
2237 | " 178 | \n",
2238 | " 0.206310 | \n",
2239 | " 0.70495 | \n",
2240 | " 0.075705 | \n",
2241 | " 0.752475 | \n",
2242 | " 0.125834 | \n",
2243 | " 0.312871 | \n",
2244 | " -0.027015 | \n",
2245 | " 0.0594059 | \n",
2246 | " 0.457426 | \n",
2247 | "
\n",
2248 | " \n",
2249 | " 23 | \n",
2250 | " ALB | \n",
2251 | " 94.85 | \n",
2252 | " 206 | \n",
2253 | " 0.339750 | \n",
2254 | " 0.823762 | \n",
2255 | " 0.036265 | \n",
2256 | " 0.691089 | \n",
2257 | " 0.528245 | \n",
2258 | " 0.918812 | \n",
2259 | " 0.120477 | \n",
2260 | " 0.673267 | \n",
2261 | " 0.776733 | \n",
2262 | "
\n",
2263 | " \n",
2264 | " 24 | \n",
2265 | " ALGN | \n",
2266 | " 309.25 | \n",
2267 | " 63 | \n",
2268 | " 0.692338 | \n",
2269 | " 0.970297 | \n",
2270 | " 0.131757 | \n",
2271 | " 0.815842 | \n",
2272 | " 0.554489 | \n",
2273 | " 0.928713 | \n",
2274 | " 0.143933 | \n",
2275 | " 0.738614 | \n",
2276 | " 0.863366 | \n",
2277 | "
\n",
2278 | " \n",
2279 | " 25 | \n",
2280 | " ALK | \n",
2281 | " 38.63 | \n",
2282 | " 507 | \n",
2283 | " -0.387440 | \n",
2284 | " 0.0633663 | \n",
2285 | " -0.438559 | \n",
2286 | " 0.0534653 | \n",
2287 | " 0.519770 | \n",
2288 | " 0.916832 | \n",
2289 | " 0.103833 | \n",
2290 | " 0.576238 | \n",
2291 | " 0.402475 | \n",
2292 | "
\n",
2293 | " \n",
2294 | " 26 | \n",
2295 | " ALL | \n",
2296 | " 99.53 | \n",
2297 | " 197 | \n",
2298 | " -0.079870 | \n",
2299 | " 0.354455 | \n",
2300 | " -0.230788 | \n",
2301 | " 0.261386 | \n",
2302 | " 0.045750 | \n",
2303 | " 0.146535 | \n",
2304 | " 0.088925 | \n",
2305 | " 0.49703 | \n",
2306 | " 0.314851 | \n",
2307 | "
\n",
2308 | " \n",
2309 | " 27 | \n",
2310 | " ALLE | \n",
2311 | " 105.13 | \n",
2312 | " 186 | \n",
2313 | " 0.071934 | \n",
2314 | " 0.542574 | \n",
2315 | " -0.246470 | \n",
2316 | " 0.241584 | \n",
2317 | " 0.112362 | \n",
2318 | " 0.277228 | \n",
2319 | " 0.024073 | \n",
2320 | " 0.20396 | \n",
2321 | " 0.316337 | \n",
2322 | "
\n",
2323 | " \n",
2324 | " 28 | \n",
2325 | " ALXN | \n",
2326 | " 107.60 | \n",
2327 | " 182 | \n",
2328 | " -0.079570 | \n",
2329 | " 0.356436 | \n",
2330 | " -0.006381 | \n",
2331 | " 0.60396 | \n",
2332 | " 0.003280 | \n",
2333 | " 0.0792079 | \n",
2334 | " -0.040910 | \n",
2335 | " 0.0455446 | \n",
2336 | " 0.271287 | \n",
2337 | "
\n",
2338 | " \n",
2339 | " 29 | \n",
2340 | " AMAT | \n",
2341 | " 68.10 | \n",
2342 | " 287 | \n",
2343 | " 0.389577 | \n",
2344 | " 0.855446 | \n",
2345 | " -0.013556 | \n",
2346 | " 0.594059 | \n",
2347 | " 0.301391 | \n",
2348 | " 0.651485 | \n",
2349 | " 0.082413 | \n",
2350 | " 0.461386 | \n",
2351 | " 0.640594 | \n",
2352 | "
\n",
2353 | " \n",
2354 | " 30 | \n",
2355 | " AMCR | \n",
2356 | " 11.52 | \n",
2357 | " 1702 | \n",
2358 | " 0.073093 | \n",
2359 | " 0.552475 | \n",
2360 | " 0.105582 | \n",
2361 | " 0.778218 | \n",
2362 | " 0.238793 | \n",
2363 | " 0.510891 | \n",
2364 | " 0.077579 | \n",
2365 | " 0.433663 | \n",
2366 | " 0.568812 | \n",
2367 | "
\n",
2368 | " \n",
2369 | " 31 | \n",
2370 | " AMD | \n",
2371 | " 85.23 | \n",
2372 | " 230 | \n",
2373 | " 1.590065 | \n",
2374 | " 0.99604 | \n",
2375 | " 0.532176 | \n",
2376 | " 0.984158 | \n",
2377 | " 0.597011 | \n",
2378 | " 0.948515 | \n",
2379 | " 0.560304 | \n",
2380 | " 0.99802 | \n",
2381 | " 0.981683 | \n",
2382 | "
\n",
2383 | " \n",
2384 | " 32 | \n",
2385 | " AME | \n",
2386 | " 104.38 | \n",
2387 | " 187 | \n",
2388 | " 0.177719 | \n",
2389 | " 0.677228 | \n",
2390 | " -0.002080 | \n",
2391 | " 0.615842 | \n",
2392 | " 0.305470 | \n",
2393 | " 0.659406 | \n",
2394 | " 0.147190 | \n",
2395 | " 0.752475 | \n",
2396 | " 0.676238 | \n",
2397 | "
\n",
2398 | " \n",
2399 | " 33 | \n",
2400 | " AMGN | \n",
2401 | " 253.49 | \n",
2402 | " 77 | \n",
2403 | " 0.176726 | \n",
2404 | " 0.671287 | \n",
2405 | " 0.086140 | \n",
2406 | " 0.764356 | \n",
2407 | " 0.020895 | \n",
2408 | " 0.112871 | \n",
2409 | " -0.034769 | \n",
2410 | " 0.0534653 | \n",
2411 | " 0.400495 | \n",
2412 | "
\n",
2413 | " \n",
2414 | " 34 | \n",
2415 | " AMP | \n",
2416 | " 166.96 | \n",
2417 | " 117 | \n",
2418 | " 0.254633 | \n",
2419 | " 0.736634 | \n",
2420 | " -0.101617 | \n",
2421 | " 0.429703 | \n",
2422 | " 0.385995 | \n",
2423 | " 0.80198 | \n",
2424 | " 0.089628 | \n",
2425 | " 0.50099 | \n",
2426 | " 0.617327 | \n",
2427 | "
\n",
2428 | " \n",
2429 | " 35 | \n",
2430 | " AMT | \n",
2431 | " 252.70 | \n",
2432 | " 77 | \n",
2433 | " 0.137500 | \n",
2434 | " 0.631683 | \n",
2435 | " -0.022213 | \n",
2436 | " 0.574257 | \n",
2437 | " 0.091099 | \n",
2438 | " 0.231683 | \n",
2439 | " -0.018941 | \n",
2440 | " 0.0871287 | \n",
2441 | " 0.381188 | \n",
2442 | "
\n",
2443 | " \n",
2444 | " 36 | \n",
2445 | " AMZN | \n",
2446 | " 3267.82 | \n",
2447 | " 6 | \n",
2448 | " 0.753428 | \n",
2449 | " 0.978218 | \n",
2450 | " 0.471720 | \n",
2451 | " 0.974257 | \n",
2452 | " 0.339324 | \n",
2453 | " 0.728713 | \n",
2454 | " 0.018781 | \n",
2455 | " 0.178218 | \n",
2456 | " 0.714851 | \n",
2457 | "
\n",
2458 | " \n",
2459 | " 37 | \n",
2460 | " ANET | \n",
2461 | " 225.98 | \n",
2462 | " 86 | \n",
2463 | " -0.065031 | \n",
2464 | " 0.374257 | \n",
2465 | " -0.093866 | \n",
2466 | " 0.449505 | \n",
2467 | " 0.011798 | \n",
2468 | " 0.0910891 | \n",
2469 | " 0.024541 | \n",
2470 | " 0.207921 | \n",
2471 | " 0.280693 | \n",
2472 | "
\n",
2473 | " \n",
2474 | " 38 | \n",
2475 | " ANSS | \n",
2476 | " 321.00 | \n",
2477 | " 61 | \n",
2478 | " 0.451591 | \n",
2479 | " 0.887129 | \n",
2480 | " 0.058949 | \n",
2481 | " 0.722772 | \n",
2482 | " 0.229415 | \n",
2483 | " 0.487129 | \n",
2484 | " 0.044242 | \n",
2485 | " 0.283168 | \n",
2486 | " 0.59505 | \n",
2487 | "
\n",
2488 | " \n",
2489 | " 39 | \n",
2490 | " ANTM | \n",
2491 | " 293.84 | \n",
2492 | " 66 | \n",
2493 | " -0.013031 | \n",
2494 | " 0.439604 | \n",
2495 | " -0.053364 | \n",
2496 | " 0.514851 | \n",
2497 | " 0.058803 | \n",
2498 | " 0.164356 | \n",
2499 | " 0.104273 | \n",
2500 | " 0.582178 | \n",
2501 | " 0.425248 | \n",
2502 | "
\n",
2503 | " \n",
2504 | " 40 | \n",
2505 | " AON | \n",
2506 | " 196.84 | \n",
2507 | " 99 | \n",
2508 | " 0.006102 | \n",
2509 | " 0.463366 | \n",
2510 | " -0.183042 | \n",
2511 | " 0.332673 | \n",
2512 | " 0.010224 | \n",
2513 | " 0.0891089 | \n",
2514 | " -0.026247 | \n",
2515 | " 0.0633663 | \n",
2516 | " 0.237129 | \n",
2517 | "
\n",
2518 | " \n",
2519 | " 41 | \n",
2520 | " AOS | \n",
2521 | " 50.31 | \n",
2522 | " 389 | \n",
2523 | " 0.101690 | \n",
2524 | " 0.59802 | \n",
2525 | " 0.141115 | \n",
2526 | " 0.825743 | \n",
2527 | " 0.260257 | \n",
2528 | " 0.564356 | \n",
2529 | " 0.032405 | \n",
2530 | " 0.231683 | \n",
2531 | " 0.55495 | \n",
2532 | "
\n",
2533 | " \n",
2534 | " 42 | \n",
2535 | " APA | \n",
2536 | " 16.40 | \n",
2537 | " 1195 | \n",
2538 | " -0.291451 | \n",
2539 | " 0.124752 | \n",
2540 | " -0.441406 | \n",
2541 | " 0.0514851 | \n",
2542 | " 0.485613 | \n",
2543 | " 0.893069 | \n",
2544 | " 0.260429 | \n",
2545 | " 0.952475 | \n",
2546 | " 0.505446 | \n",
2547 | "
\n",
2548 | " \n",
2549 | " 43 | \n",
2550 | " APD | \n",
2551 | " 295.10 | \n",
2552 | " 66 | \n",
2553 | " 0.242925 | \n",
2554 | " 0.728713 | \n",
2555 | " 0.124499 | \n",
2556 | " 0.807921 | \n",
2557 | " 0.273219 | \n",
2558 | " 0.592079 | \n",
2559 | " 0.040389 | \n",
2560 | " 0.265347 | \n",
2561 | " 0.598515 | \n",
2562 | "
\n",
2563 | " \n",
2564 | " 44 | \n",
2565 | " APH | \n",
2566 | " 110.83 | \n",
2567 | " 176 | \n",
2568 | " 0.255236 | \n",
2569 | " 0.738614 | \n",
2570 | " 0.053364 | \n",
2571 | " 0.714851 | \n",
2572 | " 0.342563 | \n",
2573 | " 0.740594 | \n",
2574 | " 0.168114 | \n",
2575 | " 0.819802 | \n",
2576 | " 0.753465 | \n",
2577 | "
\n",
2578 | " \n",
2579 | " 45 | \n",
2580 | " APTV | \n",
2581 | " 91.40 | \n",
2582 | " 214 | \n",
2583 | " 0.072118 | \n",
2584 | " 0.544554 | \n",
2585 | " -0.011731 | \n",
2586 | " 0.60198 | \n",
2587 | " 0.490744 | \n",
2588 | " 0.89505 | \n",
2589 | " 0.172564 | \n",
2590 | " 0.829703 | \n",
2591 | " 0.717822 | \n",
2592 | "
\n",
2593 | " \n",
2594 | " 46 | \n",
2595 | " ARE | \n",
2596 | " 174.20 | \n",
2597 | " 112 | \n",
2598 | " 0.208731 | \n",
2599 | " 0.706931 | \n",
2600 | " -0.001613 | \n",
2601 | " 0.619802 | \n",
2602 | " 0.214849 | \n",
2603 | " 0.465347 | \n",
2604 | " 0.078630 | \n",
2605 | " 0.441584 | \n",
2606 | " 0.558416 | \n",
2607 | "
\n",
2608 | " \n",
2609 | " 47 | \n",
2610 | " ATO | \n",
2611 | " 108.98 | \n",
2612 | " 179 | \n",
2613 | " -0.039357 | \n",
2614 | " 0.415842 | \n",
2615 | " -0.125705 | \n",
2616 | " 0.4 | \n",
2617 | " 0.089492 | \n",
2618 | " 0.229703 | \n",
2619 | " 0.053203 | \n",
2620 | " 0.306931 | \n",
2621 | " 0.338119 | \n",
2622 | "
\n",
2623 | " \n",
2624 | " 48 | \n",
2625 | " ATVI | \n",
2626 | " 82.88 | \n",
2627 | " 236 | \n",
2628 | " 0.706749 | \n",
2629 | " 0.976238 | \n",
2630 | " 0.310780 | \n",
2631 | " 0.950495 | \n",
2632 | " 0.110910 | \n",
2633 | " 0.273267 | \n",
2634 | " 0.048812 | \n",
2635 | " 0.289109 | \n",
2636 | " 0.622277 | \n",
2637 | "
\n",
2638 | " \n",
2639 | " 49 | \n",
2640 | " AVB | \n",
2641 | " 156.62 | \n",
2642 | " 125 | \n",
2643 | " -0.263600 | \n",
2644 | " 0.162376 | \n",
2645 | " -0.336301 | \n",
2646 | " 0.120792 | \n",
2647 | " 0.012586 | \n",
2648 | " 0.0930693 | \n",
2649 | " 0.005162 | \n",
2650 | " 0.142574 | \n",
2651 | " 0.129703 | \n",
2652 | "
\n",
2653 | " \n",
2654 | " 50 | \n",
2655 | " AVGO | \n",
2656 | " 336.73 | \n",
2657 | " N/A | \n",
2658 | " 0.177460 | \n",
2659 | " 0.675248 | \n",
2660 | " 0.030386 | \n",
2661 | " 0.679208 | \n",
2662 | " 0.278870 | \n",
2663 | " 0.60198 | \n",
2664 | " 0.074210 | \n",
2665 | " 0.405941 | \n",
2666 | " 0.590594 | \n",
2667 | "
\n",
2668 | " \n",
2669 | "
\n",
2670 | "
"
2671 | ],
2672 | "text/plain": [
2673 | " Ticker Price Number of Shares to Buy One-Year Price Return \\\n",
2674 | "0 A 98.19 199 0.444090 \n",
2675 | "1 AAL 13.89 1411 -0.526494 \n",
2676 | "2 AAP 161.13 121 0.088066 \n",
2677 | "3 AAPL 467.65 41 1.171724 \n",
2678 | "4 ABBV 97.29 201 0.478770 \n",
2679 | "5 ABC 104.97 186 0.163705 \n",
2680 | "6 ABMD 305.78 64 0.546138 \n",
2681 | "7 ABT 102.94 190 0.161073 \n",
2682 | "8 ACN 237.88 82 0.197484 \n",
2683 | "9 ADBE 453.91 43 0.532839 \n",
2684 | "10 ADI 120.52 162 0.056525 \n",
2685 | "11 ADM 46.62 420 0.178916 \n",
2686 | "12 ADP 139.82 140 -0.174896 \n",
2687 | "13 ADSK 234.20 83 0.528696 \n",
2688 | "14 AEE 85.23 230 0.073499 \n",
2689 | "15 AEP 87.88 223 -0.069760 \n",
2690 | "16 AES 17.54 1117 0.141966 \n",
2691 | "17 AFL 39.27 499 -0.298263 \n",
2692 | "18 AIG 31.76 617 -0.468456 \n",
2693 | "19 AIV 39.00 502 -0.260956 \n",
2694 | "20 AIZ 129.93 150 0.016575 \n",
2695 | "21 AJG 111.00 176 0.195680 \n",
2696 | "22 AKAM 110.00 178 0.206310 \n",
2697 | "23 ALB 94.85 206 0.339750 \n",
2698 | "24 ALGN 309.25 63 0.692338 \n",
2699 | "25 ALK 38.63 507 -0.387440 \n",
2700 | "26 ALL 99.53 197 -0.079870 \n",
2701 | "27 ALLE 105.13 186 0.071934 \n",
2702 | "28 ALXN 107.60 182 -0.079570 \n",
2703 | "29 AMAT 68.10 287 0.389577 \n",
2704 | "30 AMCR 11.52 1702 0.073093 \n",
2705 | "31 AMD 85.23 230 1.590065 \n",
2706 | "32 AME 104.38 187 0.177719 \n",
2707 | "33 AMGN 253.49 77 0.176726 \n",
2708 | "34 AMP 166.96 117 0.254633 \n",
2709 | "35 AMT 252.70 77 0.137500 \n",
2710 | "36 AMZN 3267.82 6 0.753428 \n",
2711 | "37 ANET 225.98 86 -0.065031 \n",
2712 | "38 ANSS 321.00 61 0.451591 \n",
2713 | "39 ANTM 293.84 66 -0.013031 \n",
2714 | "40 AON 196.84 99 0.006102 \n",
2715 | "41 AOS 50.31 389 0.101690 \n",
2716 | "42 APA 16.40 1195 -0.291451 \n",
2717 | "43 APD 295.10 66 0.242925 \n",
2718 | "44 APH 110.83 176 0.255236 \n",
2719 | "45 APTV 91.40 214 0.072118 \n",
2720 | "46 ARE 174.20 112 0.208731 \n",
2721 | "47 ATO 108.98 179 -0.039357 \n",
2722 | "48 ATVI 82.88 236 0.706749 \n",
2723 | "49 AVB 156.62 125 -0.263600 \n",
2724 | "50 AVGO 336.73 N/A 0.177460 \n",
2725 | "\n",
2726 | " One-Year Return Percentile Six-Month Price Return \\\n",
2727 | "0 0.885149 0.147456 \n",
2728 | "1 0.0237624 -0.564540 \n",
2729 | "2 0.578218 0.148378 \n",
2730 | "3 0.992079 0.401695 \n",
2731 | "4 0.89703 0.001711 \n",
2732 | "5 0.651485 0.100112 \n",
2733 | "6 0.942574 0.874783 \n",
2734 | "7 0.647525 0.139130 \n",
2735 | "8 0.69505 0.085519 \n",
2736 | "9 0.934653 0.196705 \n",
2737 | "10 0.522772 0.002920 \n",
2738 | "11 0.679208 -0.018869 \n",
2739 | "12 0.243564 -0.227657 \n",
2740 | "13 0.928713 0.116990 \n",
2741 | "14 0.556436 -0.044706 \n",
2742 | "15 0.368317 -0.186159 \n",
2743 | "16 0.635644 -0.177281 \n",
2744 | "17 0.116832 -0.283044 \n",
2745 | "18 0.0356436 -0.398200 \n",
2746 | "19 0.168317 -0.323003 \n",
2747 | "20 0.475248 -0.116835 \n",
2748 | "21 0.689109 -0.011750 \n",
2749 | "22 0.70495 0.075705 \n",
2750 | "23 0.823762 0.036265 \n",
2751 | "24 0.970297 0.131757 \n",
2752 | "25 0.0633663 -0.438559 \n",
2753 | "26 0.354455 -0.230788 \n",
2754 | "27 0.542574 -0.246470 \n",
2755 | "28 0.356436 -0.006381 \n",
2756 | "29 0.855446 -0.013556 \n",
2757 | "30 0.552475 0.105582 \n",
2758 | "31 0.99604 0.532176 \n",
2759 | "32 0.677228 -0.002080 \n",
2760 | "33 0.671287 0.086140 \n",
2761 | "34 0.736634 -0.101617 \n",
2762 | "35 0.631683 -0.022213 \n",
2763 | "36 0.978218 0.471720 \n",
2764 | "37 0.374257 -0.093866 \n",
2765 | "38 0.887129 0.058949 \n",
2766 | "39 0.439604 -0.053364 \n",
2767 | "40 0.463366 -0.183042 \n",
2768 | "41 0.59802 0.141115 \n",
2769 | "42 0.124752 -0.441406 \n",
2770 | "43 0.728713 0.124499 \n",
2771 | "44 0.738614 0.053364 \n",
2772 | "45 0.544554 -0.011731 \n",
2773 | "46 0.706931 -0.001613 \n",
2774 | "47 0.415842 -0.125705 \n",
2775 | "48 0.976238 0.310780 \n",
2776 | "49 0.162376 -0.336301 \n",
2777 | "50 0.675248 0.030386 \n",
2778 | "\n",
2779 | " Six-Month Return Percentile Three-Month Price Return \\\n",
2780 | "0 0.837624 0.221461 \n",
2781 | "1 0.0158416 0.509431 \n",
2782 | "2 0.839604 0.295700 \n",
2783 | "3 0.968317 0.474900 \n",
2784 | "4 0.629703 0.077880 \n",
2785 | "5 0.774257 0.241867 \n",
2786 | "6 0.99802 0.645795 \n",
2787 | "7 0.823762 0.093917 \n",
2788 | "8 0.762376 0.279570 \n",
2789 | "9 0.879208 0.252382 \n",
2790 | "10 0.631683 0.149173 \n",
2791 | "11 0.580198 0.328413 \n",
2792 | "12 0.265347 0.037317 \n",
2793 | "13 0.794059 0.312257 \n",
2794 | "14 0.538614 0.202938 \n",
2795 | "15 0.326733 0.084255 \n",
2796 | "16 0.336634 0.510037 \n",
2797 | "17 0.190099 0.178662 \n",
2798 | "18 0.0673267 0.242767 \n",
2799 | "19 0.142574 0.096556 \n",
2800 | "20 0.40396 0.418900 \n",
2801 | "21 0.6 0.253483 \n",
2802 | "22 0.752475 0.125834 \n",
2803 | "23 0.691089 0.528245 \n",
2804 | "24 0.815842 0.554489 \n",
2805 | "25 0.0534653 0.519770 \n",
2806 | "26 0.261386 0.045750 \n",
2807 | "27 0.241584 0.112362 \n",
2808 | "28 0.60396 0.003280 \n",
2809 | "29 0.594059 0.301391 \n",
2810 | "30 0.778218 0.238793 \n",
2811 | "31 0.984158 0.597011 \n",
2812 | "32 0.615842 0.305470 \n",
2813 | "33 0.764356 0.020895 \n",
2814 | "34 0.429703 0.385995 \n",
2815 | "35 0.574257 0.091099 \n",
2816 | "36 0.974257 0.339324 \n",
2817 | "37 0.449505 0.011798 \n",
2818 | "38 0.722772 0.229415 \n",
2819 | "39 0.514851 0.058803 \n",
2820 | "40 0.332673 0.010224 \n",
2821 | "41 0.825743 0.260257 \n",
2822 | "42 0.0514851 0.485613 \n",
2823 | "43 0.807921 0.273219 \n",
2824 | "44 0.714851 0.342563 \n",
2825 | "45 0.60198 0.490744 \n",
2826 | "46 0.619802 0.214849 \n",
2827 | "47 0.4 0.089492 \n",
2828 | "48 0.950495 0.110910 \n",
2829 | "49 0.120792 0.012586 \n",
2830 | "50 0.679208 0.278870 \n",
2831 | "\n",
2832 | " Three-Month Return Percentile One-Month Price Return \\\n",
2833 | "0 0.473267 0.093820 \n",
2834 | "1 0.908911 0.172430 \n",
2835 | "2 0.643564 0.144608 \n",
2836 | "3 0.887129 0.189840 \n",
2837 | "4 0.19802 -0.024533 \n",
2838 | "5 0.522772 0.066990 \n",
2839 | "6 0.954455 0.145153 \n",
2840 | "7 0.233663 0.081815 \n",
2841 | "8 0.605941 0.067045 \n",
2842 | "9 0.550495 0.006829 \n",
2843 | "10 0.360396 0.017244 \n",
2844 | "11 0.70495 0.123837 \n",
2845 | "12 0.134653 -0.042030 \n",
2846 | "13 0.677228 -0.001259 \n",
2847 | "14 0.447525 0.086675 \n",
2848 | "15 0.217822 -0.007456 \n",
2849 | "16 0.910891 0.186968 \n",
2850 | "17 0.4 0.082055 \n",
2851 | "18 0.528713 0.042150 \n",
2852 | "19 0.243564 0.004109 \n",
2853 | "20 0.839604 0.254327 \n",
2854 | "21 0.554455 0.092902 \n",
2855 | "22 0.312871 -0.027015 \n",
2856 | "23 0.918812 0.120477 \n",
2857 | "24 0.928713 0.143933 \n",
2858 | "25 0.916832 0.103833 \n",
2859 | "26 0.146535 0.088925 \n",
2860 | "27 0.277228 0.024073 \n",
2861 | "28 0.0792079 -0.040910 \n",
2862 | "29 0.651485 0.082413 \n",
2863 | "30 0.510891 0.077579 \n",
2864 | "31 0.948515 0.560304 \n",
2865 | "32 0.659406 0.147190 \n",
2866 | "33 0.112871 -0.034769 \n",
2867 | "34 0.80198 0.089628 \n",
2868 | "35 0.231683 -0.018941 \n",
2869 | "36 0.728713 0.018781 \n",
2870 | "37 0.0910891 0.024541 \n",
2871 | "38 0.487129 0.044242 \n",
2872 | "39 0.164356 0.104273 \n",
2873 | "40 0.0891089 -0.026247 \n",
2874 | "41 0.564356 0.032405 \n",
2875 | "42 0.893069 0.260429 \n",
2876 | "43 0.592079 0.040389 \n",
2877 | "44 0.740594 0.168114 \n",
2878 | "45 0.89505 0.172564 \n",
2879 | "46 0.465347 0.078630 \n",
2880 | "47 0.229703 0.053203 \n",
2881 | "48 0.273267 0.048812 \n",
2882 | "49 0.0930693 0.005162 \n",
2883 | "50 0.60198 0.074210 \n",
2884 | "\n",
2885 | " One-Month Return Percentile HQM Score \n",
2886 | "0 0.530693 0.681683 \n",
2887 | "1 0.827723 0.444059 \n",
2888 | "2 0.742574 0.70099 \n",
2889 | "3 0.879208 0.931683 \n",
2890 | "4 0.0693069 0.448515 \n",
2891 | "5 0.364356 0.578218 \n",
2892 | "6 0.746535 0.910396 \n",
2893 | "7 0.457426 0.540594 \n",
2894 | "8 0.366337 0.607426 \n",
2895 | "9 0.150495 0.628713 \n",
2896 | "10 0.174257 0.422277 \n",
2897 | "11 0.687129 0.662871 \n",
2898 | "12 0.0435644 0.171782 \n",
2899 | "13 0.122772 0.630693 \n",
2900 | "14 0.483168 0.506436 \n",
2901 | "15 0.106931 0.25495 \n",
2902 | "16 0.873267 0.689109 \n",
2903 | "17 0.459406 0.291584 \n",
2904 | "18 0.271287 0.225743 \n",
2905 | "19 0.140594 0.173762 \n",
2906 | "20 0.948515 0.666832 \n",
2907 | "21 0.520792 0.591089 \n",
2908 | "22 0.0594059 0.457426 \n",
2909 | "23 0.673267 0.776733 \n",
2910 | "24 0.738614 0.863366 \n",
2911 | "25 0.576238 0.402475 \n",
2912 | "26 0.49703 0.314851 \n",
2913 | "27 0.20396 0.316337 \n",
2914 | "28 0.0455446 0.271287 \n",
2915 | "29 0.461386 0.640594 \n",
2916 | "30 0.433663 0.568812 \n",
2917 | "31 0.99802 0.981683 \n",
2918 | "32 0.752475 0.676238 \n",
2919 | "33 0.0534653 0.400495 \n",
2920 | "34 0.50099 0.617327 \n",
2921 | "35 0.0871287 0.381188 \n",
2922 | "36 0.178218 0.714851 \n",
2923 | "37 0.207921 0.280693 \n",
2924 | "38 0.283168 0.59505 \n",
2925 | "39 0.582178 0.425248 \n",
2926 | "40 0.0633663 0.237129 \n",
2927 | "41 0.231683 0.55495 \n",
2928 | "42 0.952475 0.505446 \n",
2929 | "43 0.265347 0.598515 \n",
2930 | "44 0.819802 0.753465 \n",
2931 | "45 0.829703 0.717822 \n",
2932 | "46 0.441584 0.558416 \n",
2933 | "47 0.306931 0.338119 \n",
2934 | "48 0.289109 0.622277 \n",
2935 | "49 0.142574 0.129703 \n",
2936 | "50 0.405941 0.590594 "
2937 | ]
2938 | },
2939 | "execution_count": 15,
2940 | "metadata": {},
2941 | "output_type": "execute_result"
2942 | }
2943 | ],
2944 | "source": [
2945 | "position_size = float(portfolio_size) / len(hqm_dataframe.index)\n",
2946 | "for i in range(0, len(hqm_dataframe['Ticker'])-1):\n",
2947 | " hqm_dataframe.loc[i, 'Number of Shares to Buy'] = math.floor(position_size / hqm_dataframe['Price'][i])\n",
2948 | "hqm_dataframe"
2949 | ]
2950 | },
2951 | {
2952 | "cell_type": "markdown",
2953 | "metadata": {},
2954 | "source": [
2955 | "## Formatting Our Excel Output\n",
2956 | "\n",
2957 | "We will be using the XlsxWriter library for Python to create nicely-formatted Excel files.\n",
2958 | "\n",
2959 | "XlsxWriter is an excellent package and offers tons of customization. However, the tradeoff for this is that the library can seem very complicated to new users. Accordingly, this section will be fairly long because I want to do a good job of explaining how XlsxWriter works."
2960 | ]
2961 | },
2962 | {
2963 | "cell_type": "code",
2964 | "execution_count": 16,
2965 | "metadata": {},
2966 | "outputs": [],
2967 | "source": [
2968 | "writer = pd.ExcelWriter('momentum_strategy.xlsx', engine='xlsxwriter')\n",
2969 | "hqm_dataframe.to_excel(writer, sheet_name='Momentum Strategy', index = False)"
2970 | ]
2971 | },
2972 | {
2973 | "cell_type": "markdown",
2974 | "metadata": {},
2975 | "source": [
2976 | "## Creating the Formats We'll Need For Our .xlsx File\n",
2977 | "\n",
2978 | "You'll recall from our first project that formats include colors, fonts, and also symbols like % and $. We'll need four main formats for our Excel document:\n",
2979 | "\n",
2980 | "* String format for tickers\n",
2981 | "* \\$XX.XX format for stock prices\n",
2982 | "* \\$XX,XXX format for market capitalization\n",
2983 | "* Integer format for the number of shares to purchase\n",
2984 | "\n",
2985 | "Since we already built our formats in the last section of this course, I've included them below for you. Run this code cell before proceeding."
2986 | ]
2987 | },
2988 | {
2989 | "cell_type": "code",
2990 | "execution_count": 17,
2991 | "metadata": {},
2992 | "outputs": [],
2993 | "source": [
2994 | "background_color = '#0a0a23'\n",
2995 | "font_color = '#ffffff'\n",
2996 | "\n",
2997 | "string_template = writer.book.add_format(\n",
2998 | " {\n",
2999 | " 'font_color': font_color,\n",
3000 | " 'bg_color': background_color,\n",
3001 | " 'border': 1\n",
3002 | " }\n",
3003 | " )\n",
3004 | "\n",
3005 | "dollar_template = writer.book.add_format(\n",
3006 | " {\n",
3007 | " 'num_format':'$0.00',\n",
3008 | " 'font_color': font_color,\n",
3009 | " 'bg_color': background_color,\n",
3010 | " 'border': 1\n",
3011 | " }\n",
3012 | " )\n",
3013 | "\n",
3014 | "integer_template = writer.book.add_format(\n",
3015 | " {\n",
3016 | " 'num_format':'0',\n",
3017 | " 'font_color': font_color,\n",
3018 | " 'bg_color': background_color,\n",
3019 | " 'border': 1\n",
3020 | " }\n",
3021 | " )\n",
3022 | "\n",
3023 | "percent_template = writer.book.add_format(\n",
3024 | " {\n",
3025 | " 'num_format':'0.0%',\n",
3026 | " 'font_color': font_color,\n",
3027 | " 'bg_color': background_color,\n",
3028 | " 'border': 1\n",
3029 | " }\n",
3030 | " )"
3031 | ]
3032 | },
3033 | {
3034 | "cell_type": "code",
3035 | "execution_count": 18,
3036 | "metadata": {},
3037 | "outputs": [],
3038 | "source": [
3039 | "column_formats = { \n",
3040 | " 'A': ['Ticker', string_template],\n",
3041 | " 'B': ['Price', dollar_template],\n",
3042 | " 'C': ['Number of Shares to Buy', integer_template],\n",
3043 | " 'D': ['One-Year Price Return', percent_template],\n",
3044 | " 'E': ['One-Year Return Percentile', percent_template],\n",
3045 | " 'F': ['Six-Month Price Return', percent_template],\n",
3046 | " 'G': ['Six-Month Return Percentile', percent_template],\n",
3047 | " 'H': ['Three-Month Price Return', percent_template],\n",
3048 | " 'I': ['Three-Month Return Percentile', percent_template],\n",
3049 | " 'J': ['One-Month Price Return', percent_template],\n",
3050 | " 'K': ['One-Month Return Percentile', percent_template],\n",
3051 | " 'L': ['HQM Score', integer_template]\n",
3052 | " }\n",
3053 | "\n",
3054 | "for column in column_formats.keys():\n",
3055 | " writer.sheets['Momentum Strategy'].set_column(f'{column}:{column}', 20, column_formats[column][1])\n",
3056 | " writer.sheets['Momentum Strategy'].write(f'{column}1', column_formats[column][0], string_template)"
3057 | ]
3058 | },
3059 | {
3060 | "cell_type": "markdown",
3061 | "metadata": {},
3062 | "source": [
3063 | "## Saving Our Excel Output\n",
3064 | "\n",
3065 | "As before, saving our Excel output is very easy:"
3066 | ]
3067 | },
3068 | {
3069 | "cell_type": "code",
3070 | "execution_count": 19,
3071 | "metadata": {},
3072 | "outputs": [],
3073 | "source": [
3074 | "writer.save()"
3075 | ]
3076 | }
3077 | ],
3078 | "metadata": {
3079 | "kernelspec": {
3080 | "display_name": "Python 3",
3081 | "language": "python",
3082 | "name": "python3"
3083 | },
3084 | "language_info": {
3085 | "codemirror_mode": {
3086 | "name": "ipython",
3087 | "version": 3
3088 | },
3089 | "file_extension": ".py",
3090 | "mimetype": "text/x-python",
3091 | "name": "python",
3092 | "nbconvert_exporter": "python",
3093 | "pygments_lexer": "ipython3",
3094 | "version": "3.8.2"
3095 | }
3096 | },
3097 | "nbformat": 4,
3098 | "nbformat_minor": 4
3099 | }
3100 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | jupyter==1.0.0
2 | jupyter-client==6.1.3
3 | jupyter-console==6.1.0
4 | jupyter-core==4.6.3
5 | numpy==1.17.4
6 | pandas==0.25.3
7 | requests==2.22.0
8 | scipy==1.5.2
9 | XlsxWriter==1.2.2
10 |
--------------------------------------------------------------------------------
/starter_files/001_equal_weight_S&P_500.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Equal-Weight S&P 500 Index Fund\n",
8 | "\n",
9 | "## Introduction & Library Imports\n",
10 | "\n",
11 | "The S&P 500 is the world's most popular stock market index. The largest fund that is benchmarked to this index is the SPDR® S&P 500® ETF Trust. It has more than US$250 billion of assets under management.\n",
12 | "\n",
13 | "The goal of this section of the course is to create a Python script that will accept the value of your portfolio and tell you how many shares of each S&P 500 constituent you should purchase to get an equal-weight version of the index fund.\n",
14 | "\n",
15 | "## Library Imports\n",
16 | "\n",
17 | "The first thing we need to do is import the open-source software libraries that we'll be using in this tutorial."
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": null,
23 | "metadata": {},
24 | "outputs": [],
25 | "source": []
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "## Importing Our List of Stocks\n",
32 | "\n",
33 | "The next thing we need to do is import the constituents of the S&P 500.\n",
34 | "\n",
35 | "These constituents change over time, so in an ideal world you would connect directly to the index provider (Standard & Poor's) and pull their real-time constituents on a regular basis.\n",
36 | "\n",
37 | "Paying for access to the index provider's API is outside of the scope of this course. \n",
38 | "\n",
39 | "There's a static version of the S&P 500 constituents available here. [Click this link to download them now](https://drive.google.com/file/d/1ZJSpbY69DVckVZlO9cC6KkgfSufybcHN/view?usp=sharing). Move this file into the `starter-files` folder so it can be accessed by other files in that directory.\n",
40 | "\n",
41 | "Now it's time to import these stocks to our Jupyter Notebook file."
42 | ]
43 | },
44 | {
45 | "cell_type": "code",
46 | "execution_count": null,
47 | "metadata": {},
48 | "outputs": [],
49 | "source": []
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "## Acquiring an API Token\n",
56 | "\n",
57 | "Now it's time to import our IEX Cloud API token. This is the data provider that we will be using throughout this course.\n",
58 | "\n",
59 | "API tokens (and other sensitive information) should be stored in a `secrets.py` file that doesn't get pushed to your local Git repository. We'll be using a sandbox API token in this course, which means that the data we'll use is randomly-generated and (more importantly) has no cost associated with it.\n",
60 | "\n",
61 | "[Click here](http://nickmccullum.com/algorithmic-trading-python/secrets.py) to download your `secrets.py` file. Move the file into the same directory as this Jupyter Notebook before proceeding."
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": []
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "metadata": {},
74 | "source": [
75 | "## Making Our First API Call\n",
76 | "\n",
77 | "Now it's time to structure our API calls to IEX cloud. \n",
78 | "\n",
79 | "We need the following information from the API:\n",
80 | "\n",
81 | "* Market capitalization for each stock\n",
82 | "* Price of each stock\n",
83 | "\n"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": null,
89 | "metadata": {},
90 | "outputs": [],
91 | "source": []
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "## Parsing Our API Call\n",
98 | "\n",
99 | "The API call that we executed in the last code block contains all of the information required to build our equal-weight S&P 500 strategy. \n",
100 | "\n",
101 | "With that said, the data isn't in a proper format yet. We need to parse it first."
102 | ]
103 | },
104 | {
105 | "cell_type": "code",
106 | "execution_count": null,
107 | "metadata": {},
108 | "outputs": [],
109 | "source": []
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {},
114 | "source": [
115 | "## Adding Our Stocks Data to a Pandas DataFrame\n",
116 | "\n",
117 | "The next thing we need to do is add our stock's price and market capitalization to a pandas DataFrame. Think of a DataFrame like the Python version of a spreadsheet. It stores tabular data."
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": null,
123 | "metadata": {},
124 | "outputs": [],
125 | "source": []
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {},
131 | "outputs": [],
132 | "source": []
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "metadata": {},
137 | "source": [
138 | "## Looping Through The Tickers in Our List of Stocks\n",
139 | "\n",
140 | "Using the same logic that we outlined above, we can pull data for all S&P 500 stocks and store their data in the DataFrame using a `for` loop."
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": null,
146 | "metadata": {},
147 | "outputs": [],
148 | "source": []
149 | },
150 | {
151 | "cell_type": "code",
152 | "execution_count": null,
153 | "metadata": {},
154 | "outputs": [],
155 | "source": []
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "metadata": {},
160 | "source": [
161 | "## Using Batch API Calls to Improve Performance\n",
162 | "\n",
163 | "Batch API calls are one of the easiest ways to improve the performance of your code.\n",
164 | "\n",
165 | "This is because HTTP requests are typically one of the slowest components of a script.\n",
166 | "\n",
167 | "Also, API providers will often give you discounted rates for using batch API calls since they are easier for the API provider to respond to.\n",
168 | "\n",
169 | "IEX Cloud limits their batch API calls to 100 tickers per request. Still, this reduces the number of API calls we'll make in this section from 500 to 5 - huge improvement! In this section, we'll split our list of stocks into groups of 100 and then make a batch API call for each group."
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": null,
175 | "metadata": {},
176 | "outputs": [],
177 | "source": []
178 | },
179 | {
180 | "cell_type": "code",
181 | "execution_count": null,
182 | "metadata": {},
183 | "outputs": [],
184 | "source": []
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {},
189 | "source": [
190 | "## Calculating the Number of Shares to Buy\n",
191 | "\n",
192 | "As you can see in the DataFrame above, we stil haven't calculated the number of shares of each stock to buy.\n",
193 | "\n",
194 | "We'll do that next."
195 | ]
196 | },
197 | {
198 | "cell_type": "code",
199 | "execution_count": null,
200 | "metadata": {},
201 | "outputs": [],
202 | "source": []
203 | },
204 | {
205 | "cell_type": "code",
206 | "execution_count": null,
207 | "metadata": {},
208 | "outputs": [],
209 | "source": []
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "metadata": {},
214 | "source": [
215 | "## Formatting Our Excel Output\n",
216 | "\n",
217 | "We will be using the XlsxWriter library for Python to create nicely-formatted Excel files.\n",
218 | "\n",
219 | "XlsxWriter is an excellent package and offers tons of customization. However, the tradeoff for this is that the library can seem very complicated to new users. Accordingly, this section will be fairly long because I want to do a good job of explaining how XlsxWriter works.\n",
220 | "\n",
221 | "### Initializing our XlsxWriter Object"
222 | ]
223 | },
224 | {
225 | "cell_type": "code",
226 | "execution_count": null,
227 | "metadata": {},
228 | "outputs": [],
229 | "source": []
230 | },
231 | {
232 | "cell_type": "markdown",
233 | "metadata": {},
234 | "source": [
235 | "### Creating the Formats We'll Need For Our `.xlsx` File\n",
236 | "\n",
237 | "Formats include colors, fonts, and also symbols like `%` and `$`. We'll need four main formats for our Excel document:\n",
238 | "* String format for tickers\n",
239 | "* \\\\$XX.XX format for stock prices\n",
240 | "* \\\\$XX,XXX format for market capitalization\n",
241 | "* Integer format for the number of shares to purchase"
242 | ]
243 | },
244 | {
245 | "cell_type": "code",
246 | "execution_count": null,
247 | "metadata": {},
248 | "outputs": [],
249 | "source": []
250 | },
251 | {
252 | "cell_type": "markdown",
253 | "metadata": {},
254 | "source": [
255 | "### Applying the Formats to the Columns of Our `.xlsx` File\n",
256 | "\n",
257 | "We can use the `set_column` method applied to the `writer.sheets['Recommended Trades']` object to apply formats to specific columns of our spreadsheets.\n",
258 | "\n",
259 | "Here's an example:\n",
260 | "\n",
261 | "```python\n",
262 | "writer.sheets['Recommended Trades'].set_column('B:B', #This tells the method to apply the format to column B\n",
263 | " 18, #This tells the method to apply a column width of 18 pixels\n",
264 | " string_template #This applies the format 'string_template' to the column\n",
265 | " )\n",
266 | "```"
267 | ]
268 | },
269 | {
270 | "cell_type": "code",
271 | "execution_count": null,
272 | "metadata": {},
273 | "outputs": [],
274 | "source": []
275 | },
276 | {
277 | "cell_type": "markdown",
278 | "metadata": {},
279 | "source": [
280 | "This code works, but it violates the software principle of \"Don't Repeat Yourself\". \n",
281 | "\n",
282 | "Let's simplify this by putting it in 2 loops:"
283 | ]
284 | },
285 | {
286 | "cell_type": "code",
287 | "execution_count": null,
288 | "metadata": {},
289 | "outputs": [],
290 | "source": []
291 | },
292 | {
293 | "cell_type": "markdown",
294 | "metadata": {},
295 | "source": [
296 | "## Saving Our Excel Output\n",
297 | "\n",
298 | "Saving our Excel file is very easy:"
299 | ]
300 | },
301 | {
302 | "cell_type": "code",
303 | "execution_count": null,
304 | "metadata": {},
305 | "outputs": [],
306 | "source": []
307 | }
308 | ],
309 | "metadata": {
310 | "kernelspec": {
311 | "display_name": "Python 3",
312 | "language": "python",
313 | "name": "python3"
314 | },
315 | "language_info": {
316 | "codemirror_mode": {
317 | "name": "ipython",
318 | "version": 3
319 | },
320 | "file_extension": ".py",
321 | "mimetype": "text/x-python",
322 | "name": "python",
323 | "nbconvert_exporter": "python",
324 | "pygments_lexer": "ipython3",
325 | "version": "3.8.2"
326 | }
327 | },
328 | "nbformat": 4,
329 | "nbformat_minor": 4
330 | }
331 |
--------------------------------------------------------------------------------
/starter_files/002_quantitative_momentum_strategy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Quantitative Momentum Strategy\n",
8 | "\n",
9 | "\"Momentum investing\" means investing in the stocks that have increased in price the most.\n",
10 | "\n",
11 | "For this project, we're going to build an investing strategy that selects the 50 stocks with the highest price momentum. From there, we will calculate recommended trades for an equal-weight portfolio of these 50 stocks.\n",
12 | "\n",
13 | "\n",
14 | "## Library Imports\n",
15 | "\n",
16 | "The first thing we need to do is import the open-source software libraries that we'll be using in this tutorial."
17 | ]
18 | },
19 | {
20 | "cell_type": "code",
21 | "execution_count": null,
22 | "metadata": {},
23 | "outputs": [],
24 | "source": []
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "metadata": {},
29 | "source": [
30 | "## Importing Our List of Stocks\n",
31 | "\n",
32 | "As before, we'll need to import our list of stocks and our API token before proceeding. Make sure the `.csv` file is still in your working directory and import it with the following command:"
33 | ]
34 | },
35 | {
36 | "cell_type": "code",
37 | "execution_count": null,
38 | "metadata": {},
39 | "outputs": [],
40 | "source": []
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "metadata": {},
45 | "source": [
46 | "## Making Our First API Call\n",
47 | "\n",
48 | "It's now time to make the first version of our momentum screener!\n",
49 | "\n",
50 | "We need to get one-year price returns for each stock in the universe. Here's how."
51 | ]
52 | },
53 | {
54 | "cell_type": "code",
55 | "execution_count": null,
56 | "metadata": {},
57 | "outputs": [],
58 | "source": []
59 | },
60 | {
61 | "cell_type": "markdown",
62 | "metadata": {},
63 | "source": [
64 | "## Parsing Our API Call\n",
65 | "\n",
66 | "This API call has all the information we need. We can parse it using the same square-bracket notation as in the first project of this course. Here is an example."
67 | ]
68 | },
69 | {
70 | "cell_type": "code",
71 | "execution_count": null,
72 | "metadata": {},
73 | "outputs": [],
74 | "source": []
75 | },
76 | {
77 | "cell_type": "markdown",
78 | "metadata": {},
79 | "source": [
80 | "## Executing A Batch API Call & Building Our DataFrame\n",
81 | "\n",
82 | "Just like in our first project, it's now time to execute several batch API calls and add the information we need to our DataFrame.\n",
83 | "\n",
84 | "We'll start by running the following code cell, which contains some code we already built last time that we can re-use for this project. More specifically, it contains a function called `chunks` that we can use to divide our list of securities into groups of 100."
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "metadata": {},
91 | "outputs": [],
92 | "source": [
93 | "# Function sourced from \n",
94 | "# https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks\n",
95 | "def chunks(lst, n):\n",
96 | " \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n",
97 | " for i in range(0, len(lst), n):\n",
98 | " yield lst[i:i + n] \n",
99 | " \n",
100 | "symbol_groups = list(chunks(stocks['Ticker'], 100))\n",
101 | "symbol_strings = []\n",
102 | "for i in range(0, len(symbol_groups)):\n",
103 | " symbol_strings.append(','.join(symbol_groups[i]))\n",
104 | "# print(symbol_strings[i])\n",
105 | "\n",
106 | "my_columns = ['Ticker', 'Price', 'One-Year Price Return', 'Number of Shares to Buy']"
107 | ]
108 | },
109 | {
110 | "cell_type": "markdown",
111 | "metadata": {},
112 | "source": [
113 | "Now we need to create a blank DataFrame and add our data to the data frame one-by-one."
114 | ]
115 | },
116 | {
117 | "cell_type": "code",
118 | "execution_count": null,
119 | "metadata": {},
120 | "outputs": [],
121 | "source": []
122 | },
123 | {
124 | "cell_type": "markdown",
125 | "metadata": {},
126 | "source": [
127 | "## Removing Low-Momentum Stocks\n",
128 | "\n",
129 | "The investment strategy that we're building seeks to identify the 50 highest-momentum stocks in the S&P 500.\n",
130 | "\n",
131 | "Because of this, the next thing we need to do is remove all the stocks in our DataFrame that fall below this momentum threshold. We'll sort the DataFrame by the stocks' one-year price return, and drop all stocks outside the top 50.\n"
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": null,
137 | "metadata": {},
138 | "outputs": [],
139 | "source": []
140 | },
141 | {
142 | "cell_type": "markdown",
143 | "metadata": {},
144 | "source": [
145 | "## Calculating the Number of Shares to Buy\n",
146 | "\n",
147 | "Just like in the last project, we now need to calculate the number of shares we need to buy. The one change we're going to make is wrapping this functionality inside a function, since we'll be using it again later in this Jupyter Notebook.\n",
148 | "\n",
149 | "Since we've already done most of the work on this, try to complete the following two code cells without watching me do it first!"
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": null,
155 | "metadata": {},
156 | "outputs": [],
157 | "source": []
158 | },
159 | {
160 | "cell_type": "code",
161 | "execution_count": null,
162 | "metadata": {},
163 | "outputs": [],
164 | "source": []
165 | },
166 | {
167 | "cell_type": "markdown",
168 | "metadata": {},
169 | "source": [
170 | "## Building a Better (and More Realistic) Momentum Strategy\n",
171 | "\n",
172 | "Real-world quantitative investment firms differentiate between \"high quality\" and \"low quality\" momentum stocks:\n",
173 | "\n",
174 | "* High-quality momentum stocks show \"slow and steady\" outperformance over long periods of time\n",
175 | "* Low-quality momentum stocks might not show any momentum for a long time, and then surge upwards.\n",
176 | "\n",
177 | "The reason why high-quality momentum stocks are preferred is because low-quality momentum can often be cause by short-term news that is unlikely to be repeated in the future (such as an FDA approval for a biotechnology company).\n",
178 | "\n",
179 | "To identify high-quality momentum, we're going to build a strategy that selects stocks from the highest percentiles of: \n",
180 | "\n",
181 | "* 1-month price returns\n",
182 | "* 3-month price returns\n",
183 | "* 6-month price returns\n",
184 | "* 1-year price returns\n",
185 | "\n",
186 | "Let's start by building our DataFrame. You'll notice that I use the abbreviation `hqm` often. It stands for `high-quality momentum`."
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": null,
192 | "metadata": {},
193 | "outputs": [],
194 | "source": []
195 | },
196 | {
197 | "cell_type": "markdown",
198 | "metadata": {},
199 | "source": [
200 | "## Calculating Momentum Percentiles\n",
201 | "\n",
202 | "We now need to calculate momentum percentile scores for every stock in the universe. More specifically, we need to calculate percentile scores for the following metrics for every stock:\n",
203 | "\n",
204 | "* `One-Year Price Return`\n",
205 | "* `Six-Month Price Return`\n",
206 | "* `Three-Month Price Return`\n",
207 | "* `One-Month Price Return`\n",
208 | "\n",
209 | "Here's how we'll do this:"
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": null,
215 | "metadata": {},
216 | "outputs": [],
217 | "source": []
218 | },
219 | {
220 | "cell_type": "markdown",
221 | "metadata": {},
222 | "source": [
223 | "## Calculating the HQM Score\n",
224 | "\n",
225 | "We'll now calculate our `HQM Score`, which is the high-quality momentum score that we'll use to filter for stocks in this investing strategy.\n",
226 | "\n",
227 | "The `HQM Score` will be the arithmetic mean of the 4 momentum percentile scores that we calculated in the last section.\n",
228 | "\n",
229 | "To calculate arithmetic mean, we will use the `mean` function from Python's built-in `statistics` module."
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": null,
235 | "metadata": {},
236 | "outputs": [],
237 | "source": []
238 | },
239 | {
240 | "cell_type": "markdown",
241 | "metadata": {},
242 | "source": [
243 | "## Selecting the 50 Best Momentum Stocks\n",
244 | "\n",
245 | "As before, we can identify the 50 best momentum stocks in our universe by sorting the DataFrame on the `HQM Score` column and dropping all but the top 50 entries."
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": null,
251 | "metadata": {},
252 | "outputs": [],
253 | "source": []
254 | },
255 | {
256 | "cell_type": "markdown",
257 | "metadata": {},
258 | "source": [
259 | "## Calculating the Number of Shares to Buy\n",
260 | "\n",
261 | "We'll use the `portfolio_input` function that we created earlier to accept our portfolio size. Then we will use similar logic in a `for` loop to calculate the number of shares to buy for each stock in our investment universe."
262 | ]
263 | },
264 | {
265 | "cell_type": "code",
266 | "execution_count": null,
267 | "metadata": {},
268 | "outputs": [],
269 | "source": []
270 | },
271 | {
272 | "cell_type": "code",
273 | "execution_count": null,
274 | "metadata": {},
275 | "outputs": [],
276 | "source": []
277 | },
278 | {
279 | "cell_type": "markdown",
280 | "metadata": {},
281 | "source": [
282 | "## Formatting Our Excel Output\n",
283 | "\n",
284 | "We will be using the XlsxWriter library for Python to create nicely-formatted Excel files.\n",
285 | "\n",
286 | "XlsxWriter is an excellent package and offers tons of customization. However, the tradeoff for this is that the library can seem very complicated to new users. Accordingly, this section will be fairly long because I want to do a good job of explaining how XlsxWriter works."
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": null,
292 | "metadata": {},
293 | "outputs": [],
294 | "source": []
295 | },
296 | {
297 | "cell_type": "markdown",
298 | "metadata": {},
299 | "source": [
300 | "## Creating the Formats We'll Need For Our .xlsx File\n",
301 | "\n",
302 | "You'll recall from our first project that formats include colors, fonts, and also symbols like % and $. We'll need four main formats for our Excel document:\n",
303 | "\n",
304 | "* String format for tickers\n",
305 | "* \\$XX.XX format for stock prices\n",
306 | "* \\$XX,XXX format for market capitalization\n",
307 | "* Integer format for the number of shares to purchase\n",
308 | "\n",
309 | "Since we already built our formats in the last section of this course, I've included them below for you. Run this code cell before proceeding."
310 | ]
311 | },
312 | {
313 | "cell_type": "code",
314 | "execution_count": null,
315 | "metadata": {},
316 | "outputs": [],
317 | "source": [
318 | "background_color = '#0a0a23'\n",
319 | "font_color = '#ffffff'\n",
320 | "\n",
321 | "string_template = writer.book.add_format(\n",
322 | " {\n",
323 | " 'font_color': font_color,\n",
324 | " 'bg_color': background_color,\n",
325 | " 'border': 1\n",
326 | " }\n",
327 | " )\n",
328 | "\n",
329 | "dollar_template = writer.book.add_format(\n",
330 | " {\n",
331 | " 'num_format':'$0.00',\n",
332 | " 'font_color': font_color,\n",
333 | " 'bg_color': background_color,\n",
334 | " 'border': 1\n",
335 | " }\n",
336 | " )\n",
337 | "\n",
338 | "integer_template = writer.book.add_format(\n",
339 | " {\n",
340 | " 'num_format':'0',\n",
341 | " 'font_color': font_color,\n",
342 | " 'bg_color': background_color,\n",
343 | " 'border': 1\n",
344 | " }\n",
345 | " )\n",
346 | "\n",
347 | "percent_template = writer.book.add_format(\n",
348 | " {\n",
349 | " 'num_format':'0.0%',\n",
350 | " 'font_color': font_color,\n",
351 | " 'bg_color': background_color,\n",
352 | " 'border': 1\n",
353 | " }\n",
354 | " )"
355 | ]
356 | },
357 | {
358 | "cell_type": "code",
359 | "execution_count": null,
360 | "metadata": {},
361 | "outputs": [],
362 | "source": []
363 | },
364 | {
365 | "cell_type": "markdown",
366 | "metadata": {},
367 | "source": [
368 | "## Saving Our Excel Output\n",
369 | "\n",
370 | "As before, saving our Excel output is very easy:"
371 | ]
372 | },
373 | {
374 | "cell_type": "code",
375 | "execution_count": null,
376 | "metadata": {},
377 | "outputs": [],
378 | "source": []
379 | }
380 | ],
381 | "metadata": {
382 | "kernelspec": {
383 | "display_name": "Python 3",
384 | "language": "python",
385 | "name": "python3"
386 | },
387 | "language_info": {
388 | "codemirror_mode": {
389 | "name": "ipython",
390 | "version": 3
391 | },
392 | "file_extension": ".py",
393 | "mimetype": "text/x-python",
394 | "name": "python",
395 | "nbconvert_exporter": "python",
396 | "pygments_lexer": "ipython3",
397 | "version": "3.8.5"
398 | }
399 | },
400 | "nbformat": 4,
401 | "nbformat_minor": 4
402 | }
403 |
--------------------------------------------------------------------------------