├── .gitignore
├── screenshots
├── dow1.png
├── dow2.png
├── fb.png
├── aapl1.png
├── aapl2.png
└── amzn1.png
├── indices
├── dow_members.csv
└── sp_members.csv
├── README.md
├── src
├── QuoteHistory.py
├── Portfolio.py
└── Stock.py
├── Examples.py
└── data
├── AAPL.csv
└── ^GSPC.csv
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 |
--------------------------------------------------------------------------------
/screenshots/dow1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/dow1.png
--------------------------------------------------------------------------------
/screenshots/dow2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/dow2.png
--------------------------------------------------------------------------------
/screenshots/fb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/fb.png
--------------------------------------------------------------------------------
/screenshots/aapl1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/aapl1.png
--------------------------------------------------------------------------------
/screenshots/aapl2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/aapl2.png
--------------------------------------------------------------------------------
/screenshots/amzn1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willsaid/investing-bot/HEAD/screenshots/amzn1.png
--------------------------------------------------------------------------------
/indices/dow_members.csv:
--------------------------------------------------------------------------------
1 | Symbol
2 | WBA
3 | CSCO
4 | PG
5 | UNH
6 | AXP
7 | PFE
8 | BA
9 | MCD
10 | V
11 | JNJ
12 | VZ
13 | DIS
14 | KO
15 | WMT
16 | TRV
17 | AAPL
18 | CAT
19 | UTX
20 | NKE
21 | MSFT
22 | CVX
23 | INTC
24 | MRK
25 | MMM
26 | XOM
27 | HD
28 | GS
29 | IBM
30 | JPM
31 | DWDP
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # investing-bot
2 | ### Predictive model for the quantitative analysis of stocks using machine learning AI
3 |
4 | ## Usage
5 | ##### Setup
6 | Make sure you have Python 3 installed.
7 | You may also need to install some libraries with:
8 | ```
9 | pip3 install scikit-learn matplotlib pandas scipy
10 | ```
11 |
12 | ##### Run
13 | Inside the directory you downloaded Investing Bot, run Examples.py:
14 | ```
15 | python3 Examples.py
16 | ```
17 | Examples.py will walk you through example usages of the Stock and Portfolio classes, like:
18 | - Analyzing and forecasting a single stock as a buy, sell, or holding opportunity
19 | - Scanning all 500 stocks in the S&P 500 index, ranking the top 20 stocks to buy right now
20 | - Optimizing the allocation percentages of stocks in your custom Portfolio to maximize risk-adjusted returns (Sharpe Ratio)
21 | - Maximizing the Sharpe Ratio of all 30 stocks in the Dow Jones Industrial Index by determining optimal percentage allocations
22 |
23 | These will also plot charts to display the data. Example charts:
24 |
25 |
26 | ## AAPL single-stock analysis and prediction:
27 |
28 |
29 |
30 |
31 |
32 | ## Dow Jones portfolio optimization:
33 |
34 |
35 |
36 |
37 |
38 | ## AMZN Analysis:
39 |
40 |
41 |
42 |
43 | ## FB Analysis:
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/QuoteHistory.py:
--------------------------------------------------------------------------------
1 | """ Fetches Quote History from Yahoo Finance by setting a cookie,
2 | downloading the parsed CSV to the data/ directory,
3 | and returning the pandas dataframe for the quote
4 | """
5 |
6 | import re
7 | from urllib.request import urlopen, Request, URLError
8 | import calendar
9 | import datetime
10 | import getopt
11 | import sys
12 | import time
13 | import pandas as pd
14 | import os
15 |
16 |
17 | crumble_link = 'https://finance.yahoo.com/quote/{0}/history?p={0}'
18 | crumble_regex = r'CrumbStore":{"crumb":"(.*?)"}'
19 | cookie_regex = r'set-cookie: (.*?); '
20 | quote_link = 'https://query1.finance.yahoo.com/v7/finance/download/{}?period1={}&period2={}&interval=1d&events={}&crumb={}'
21 |
22 |
23 | def get_crumble_and_cookie(symbol):
24 | link = crumble_link.format(symbol)
25 | response = urlopen(link)
26 | match = re.search(cookie_regex, str(response.info()))
27 | cookie_str = match.group(1)
28 | text = response.read().decode("utf-8")
29 | match = re.search(crumble_regex, text)
30 | if match is not None:
31 | crumble_str = match.group(1)
32 | return crumble_str , cookie_str
33 |
34 |
35 | def download_quote(symbol, date_from, date_to, events):
36 | time_stamp_from = calendar.timegm(datetime.datetime.strptime(date_from, "%Y-%m-%d").timetuple())
37 | next_day = datetime.datetime.strptime(date_to, "%Y-%m-%d") + datetime.timedelta(days=1)
38 | time_stamp_to = calendar.timegm(next_day.timetuple())
39 | attempts = 0
40 | while attempts < 5:
41 | if get_crumble_and_cookie(symbol) is None: return None
42 | crumble_str, cookie_str = get_crumble_and_cookie(symbol)
43 | link = quote_link.format(symbol, time_stamp_from, time_stamp_to, events,crumble_str)
44 | r = Request(link, headers={'Cookie': cookie_str})
45 | try:
46 | response = urlopen(r)
47 | text = response.read()
48 | print ("{} downloaded".format(symbol))
49 | return text
50 | except URLError:
51 | print ("{} failed at attempt # {}".format(symbol, attempts))
52 | attempts += 1
53 | time.sleep(2 * attempts)
54 | return b''
55 |
56 |
57 | def get_data(symbol_val, is_fresh, start=None, end=None, days=None):
58 | """ Returns pandas dataframe of Date, Adj Close, and Volume from Yahoo Finance, or None if not available.
59 | End date can be assumed to be today.
60 | Start date is automatically 140 days ago, or about 100 market days.
61 | Days ex: 365 for the past calendar year.
62 | """
63 | symbol_val = symbol_val.replace('.', '-') # BRK.B -> BRK-B
64 | event_val = "history" # historical data
65 | output_val = "data/" + symbol_val + '.csv' # file destination
66 |
67 | # set begin and end date time strings
68 | if start is None and end is None:
69 | from_val, to_val = get_date(days)
70 | elif end is None:
71 | from_val = start
72 | to_val = get_date_string(datetime.datetime.now())
73 | else:
74 | from_val = start
75 | to_val = end
76 |
77 | # use old data if present and if is_fresh
78 | if not is_fresh and os.path.isfile(output_val):
79 | try:
80 | return pd.read_csv(output_val, index_col='Date', parse_dates=True, usecols=['Date', 'Adj Close', 'Volume'], na_values=['NaN']).dropna()[from_val: to_val]
81 | except Exception:
82 | print('Failed to read from {}. Now fetching {} fresh from network.'.format(output_val, symbol_val))
83 |
84 | # Download data from Yahoo
85 | print ("downloading {}".format(symbol_val))
86 | csv = download_quote(symbol_val, from_val, to_val, event_val)
87 | if csv is not None:
88 | with open(output_val, 'wb') as f:
89 | f.write(csv)
90 | print ("{} written to {}".format(symbol_val, output_val))
91 | return pd.read_csv(output_val, index_col='Date', parse_dates=True, usecols=['Date', 'Adj Close', 'Volume'], na_values=['NaN']).dropna()[from_val: to_val]
92 |
93 |
94 | def get_date(days):
95 | """ Returns starting and ending date (like '2018-08-15') given amount of days to go back """
96 | if days is None: days = 140 # about 100 trading days
97 | now = datetime.datetime.now()
98 | past = now - datetime.timedelta(days=days)
99 | return get_date_string(past), get_date_string(now)
100 |
101 | def get_date_string(datetime):
102 | return '{}-{}-{}'.format(datetime.year, datetime.month, datetime.day)
103 |
--------------------------------------------------------------------------------
/Examples.py:
--------------------------------------------------------------------------------
1 | """ Example usages of the Stock and Portfolio classes.
2 |
3 | Usage: 'python3 Examples.py'
4 | """
5 |
6 | import pandas as pd
7 | import numpy as np
8 |
9 | # local
10 | from src import Stock, Portfolio
11 |
12 |
13 |
14 | def best_in_sp500(fresh):
15 | """ Finds the top 20 stocks to buy right now in the S&P 500 """
16 | stocks = []
17 | df = pd.read_csv('indices/sp_members.csv')
18 | for sym in df['Symbol']:
19 | print(sym)
20 | try:
21 | x = Stock.Stock(sym, 0, 0, fresh, plot=False)
22 | x.buy_or_sell(debug=False)
23 | stocks.append(x)
24 | except Exception:
25 | print('Failed to determine for {}'.format(sym))
26 |
27 | stocks.sort(key=lambda x: x.buying_certainty, reverse=True)
28 | print('\n\n\nSORTED STOCKS:\n\n\n')
29 | sorted = stocks[:20]
30 | for x in sorted:
31 | print(x.debug)
32 |
33 | def sp_over_time(fresh):
34 | """ performs stock_over_time() on all stocks in the S&P 500, then ranks the top 20 best stocks to buy
35 | given historical in multiple time scales like 5 years, 1 year, and 100 days
36 | """
37 | stocks = []
38 | df = pd.read_csv('indices/sp_members.csv')
39 | fresh_copy = fresh
40 | fresh_sp = fresh
41 | for sym in df['Symbol']:
42 | print(sym)
43 | fresh_copy = fresh
44 | try:
45 | day_lengths = [x * (7./5) for x in [365*5, 365, 100]] # * 7/5 for just trading days
46 | stock_intervals = [] # the list of, say, only AAPL's 5 year, 1 year, and 100 day analysis
47 | for days_length in day_lengths:
48 | x = Stock.Stock(sym, 0, 0, fresh_copy, fresh_sp=fresh_sp, plot=False, days=days_length)
49 | x.buy_or_sell(debug=False)
50 | fresh_copy = False # so it doesn't change existing csv data of 5 years
51 | fresh_sp = False # so it doesn't fetch GSPC (s&p 500) again each time
52 | stock_intervals.append(x)
53 | stocks.append(stock_intervals)
54 | except Exception:
55 | print('Failed to determine for {}'.format(sym))
56 |
57 | # sort the stock list of interval lists by the mean buying certainty of each stock's intervals
58 | stocks.sort(key=lambda x: np.array([y.buying_certainty for y in x]).mean(), reverse=True)
59 | print('\n\n\nSORTED STOCKS:\n\n\n')
60 | sorted = stocks[:20] # only show top 20
61 | for stock_interval in sorted:
62 | for stock in stock_interval:
63 | print(stock.debug)
64 |
65 | def optimized_dowjones(fresh):
66 | """ The best possible allocations of all 30 stocks in the Dow Jones Index over the past year """
67 | stocks = pd.read_csv('indices/dow_members.csv')['Symbol'].values
68 | guess_allocations = 30 * [1./30] # even distribution of 1/30 for each stock
69 | port = Portfolio.Portfolio(1, stocks, guess_allocations, fresh=fresh, days=365).debug()
70 |
71 |
72 | def custom_portfolio(fresh):
73 | """ Example portfolio optimization and analysis of 4 stocks over the past year """
74 | Portfolio.Portfolio(1, ['AAPL', 'GOOGL', 'AMZN', 'FB'], [.25, .25, .25, .25], fresh=fresh, days=365).debug()
75 |
76 |
77 | def single_stock(fresh):
78 | """ Analyzes and predicts future prices for a stock given performance in the past 100 trading days """
79 | symbol = input('\nSymbol: ')
80 | shares = int(input('Current Shares: '))
81 | avg_paid = int(input('Avg Price Paid: ')) if int(shares) > 0 else 0
82 |
83 | Stock.Stock(symbol, shares, avg_paid, fresh).buy_or_sell()
84 |
85 |
86 | def stock_over_time(fresh):
87 | """ Same as single_stock(), but incorporates several intervals of data:
88 | Past 5 trading years, past 1 trading year, and past 100 trading days
89 | """
90 | symbol = input('\nSymbol: ')
91 | shares = int(input('Current Shares: '))
92 | avg_paid = int(input('Avg Price Paid: ')) if int(shares) > 0 else 0
93 |
94 | # past 5 years, 1 year, 100 trading days, 1 trading week
95 | day_lengths = [x * (7./5) for x in [365*5, 365, 100]] # for just trading days
96 | for days_length in day_lengths:
97 | Stock.Stock(symbol, shares, avg_paid, fresh, days=days_length).buy_or_sell()
98 | fresh = False # so it doesn't change existing csv data of 5 years
99 |
100 | if __name__ == '__main__':
101 | option = input('Choose example:\n \'b\': Best Stocks to Buy in S&P 500\n \'o\': Optimized Dow Jones Portfolio\n \'s\': Single Stock Prediction\n \'c\': Custom Portfolio Analysis\n \'t\': Stock Analysis over Time\n \'sp\': Best S&P Stocks over Time\n')
102 | fresh = input('Need Fresh data? (Default to yes) [\'y\' or \'n\']\n') == 'y'
103 |
104 | if option == 'b':
105 | best_in_sp500(fresh)
106 | elif option == 'o':
107 | optimized_dowjones(fresh)
108 | elif option == 's':
109 | single_stock(fresh)
110 | elif option == 'c':
111 | custom_portfolio(fresh)
112 | elif option == 't':
113 | stock_over_time(fresh)
114 | elif option == 'sp':
115 | sp_over_time(fresh)
116 | #
117 |
--------------------------------------------------------------------------------
/data/AAPL.csv:
--------------------------------------------------------------------------------
1 | Date,Open,High,Low,Close,Adj Close,Volume
2 | 2018-04-03,167.639999,168.750000,164.880005,168.389999,167.156921,30278000
3 | 2018-04-04,164.880005,172.009995,164.770004,171.610001,170.353363,34605500
4 | 2018-04-05,172.580002,174.229996,172.080002,172.800003,171.534637,26933200
5 | 2018-04-06,170.970001,172.479996,168.199997,168.380005,167.147018,35005300
6 | 2018-04-09,169.880005,173.089996,169.850006,170.050003,168.804779,29017700
7 | 2018-04-10,173.000000,174.000000,171.529999,173.250000,171.981339,28408600
8 | 2018-04-11,172.229996,173.919998,171.699997,172.440002,171.177277,22431600
9 | 2018-04-12,173.410004,175.000000,173.039993,174.139999,172.864822,22889300
10 | 2018-04-13,174.779999,175.839996,173.850006,174.729996,173.450500,25124300
11 | 2018-04-16,175.029999,176.190002,174.830002,175.820007,174.532532,21578400
12 | 2018-04-17,176.490005,178.940002,176.410004,178.240005,176.934799,26605400
13 | 2018-04-18,177.809998,178.820007,176.880005,177.839996,176.537720,20754500
14 | 2018-04-19,173.759995,175.389999,172.660004,172.800003,171.534637,34808800
15 | 2018-04-20,170.600006,171.220001,165.429993,165.720001,164.506470,65491100
16 | 2018-04-23,166.830002,166.919998,164.089996,165.240005,164.029999,36515500
17 | 2018-04-24,165.669998,166.330002,161.220001,162.940002,161.746841,33692000
18 | 2018-04-25,162.619995,165.419998,162.410004,163.649994,162.451630,28382100
19 | 2018-04-26,164.119995,165.729996,163.369995,164.220001,163.017471,27963000
20 | 2018-04-27,164.000000,164.330002,160.630005,162.320007,161.131378,35655800
21 | 2018-04-30,162.130005,167.259995,161.839996,165.259995,164.049835,42427400
22 | 2018-05-01,166.410004,169.199997,165.270004,169.100006,167.861725,53569400
23 | 2018-05-02,175.229996,177.750000,173.800003,176.570007,175.277039,66539400
24 | 2018-05-03,175.880005,177.500000,174.440002,176.889999,175.594681,34068200
25 | 2018-05-04,178.250000,184.250000,178.169998,183.830002,182.483856,56201300
26 | 2018-05-07,185.179993,187.669998,184.750000,185.160004,183.804123,42451400
27 | 2018-05-08,184.990005,186.220001,183.669998,186.050003,184.687607,28402800
28 | 2018-05-09,186.550003,187.399994,185.220001,187.360001,185.988022,23211200
29 | 2018-05-10,187.740005,190.369995,187.649994,190.039993,188.648392,27989300
30 | 2018-05-11,189.490005,190.059998,187.449997,188.589996,187.930908,26212200
31 | 2018-05-14,189.009995,189.529999,187.860001,188.149994,187.492432,20778800
32 | 2018-05-15,186.779999,187.070007,185.100006,186.440002,185.788422,23695200
33 | 2018-05-16,186.070007,188.460007,186.000000,188.179993,187.522324,19183100
34 | 2018-05-17,188.000000,188.910004,186.360001,186.990005,186.336502,17294000
35 | 2018-05-18,187.190002,187.809998,186.130005,186.309998,185.658875,18297700
36 | 2018-05-21,188.000000,189.270004,186.910004,187.630005,186.974258,18400800
37 | 2018-05-22,188.380005,188.880005,186.779999,187.160004,186.505905,15240700
38 | 2018-05-23,186.350006,188.500000,185.759995,188.360001,187.701706,19467900
39 | 2018-05-24,188.770004,188.839996,186.210007,188.149994,187.492432,20401000
40 | 2018-05-25,188.229996,189.649994,187.649994,188.580002,187.920944,17461000
41 | 2018-05-29,187.600006,188.750000,186.869995,187.899994,187.243317,22369000
42 | 2018-05-30,187.720001,188.000000,186.779999,187.500000,186.844711,18690500
43 | 2018-05-31,187.220001,188.229996,186.139999,186.869995,186.216904,27482800
44 | 2018-06-01,187.990005,190.259995,187.750000,190.240005,189.575150,23250400
45 | 2018-06-04,191.639999,193.419998,191.350006,191.830002,191.159576,26132000
46 | 2018-06-05,193.070007,193.940002,192.360001,193.309998,192.634399,21566000
47 | 2018-06-06,193.630005,194.080002,191.919998,193.979996,193.302063,20933600
48 | 2018-06-07,194.139999,194.199997,192.339996,193.460007,192.783890,21347200
49 | 2018-06-08,191.169998,192.000000,189.770004,191.699997,191.030029,26656800
50 | 2018-06-11,191.350006,191.970001,190.210007,191.229996,190.561676,18308500
51 | 2018-06-12,191.389999,192.610001,191.149994,192.279999,191.608002,16911100
52 | 2018-06-13,192.419998,192.880005,190.440002,190.699997,190.033524,21638400
53 | 2018-06-14,191.550003,191.570007,190.220001,190.800003,190.133179,21610100
54 | 2018-06-15,190.029999,190.160004,188.259995,188.839996,188.180023,61719200
55 | 2018-06-18,187.880005,189.220001,187.199997,188.740005,188.080383,18484900
56 | 2018-06-19,185.139999,186.330002,183.449997,185.690002,185.041046,33578500
57 | 2018-06-20,186.350006,187.199997,185.729996,186.500000,185.848206,20628700
58 | 2018-06-21,187.250000,188.350006,184.940002,185.460007,184.811844,25711900
59 | 2018-06-22,186.119995,186.149994,184.699997,184.919998,184.273727,27200400
60 | 2018-06-25,183.399994,184.919998,180.729996,182.169998,181.533340,31663100
61 | 2018-06-26,182.990005,186.529999,182.539993,184.429993,183.785431,24569200
62 | 2018-06-27,185.229996,187.279999,184.029999,184.160004,183.516388,25285300
63 | 2018-06-28,184.100006,186.210007,183.800003,185.500000,184.851700,17365200
64 | 2018-06-29,186.289993,187.190002,182.910004,185.110001,184.463074,22737700
65 | 2018-07-02,183.820007,187.300003,183.419998,187.179993,186.525818,17731300
66 | 2018-07-03,187.789993,187.949997,183.539993,183.919998,183.277222,13954800
67 | 2018-07-05,185.259995,186.410004,184.279999,185.399994,184.752045,16604200
68 | 2018-07-06,185.419998,188.429993,185.199997,187.970001,187.313080,17485200
69 | 2018-07-09,189.500000,190.679993,189.300003,190.580002,189.913956,19756600
70 | 2018-07-10,190.710007,191.279999,190.179993,190.350006,189.684753,15939100
71 | 2018-07-11,188.500000,189.779999,187.610001,187.880005,187.223389,18831500
72 | 2018-07-12,189.529999,191.410004,189.309998,191.029999,190.362381,18041100
73 | 2018-07-13,191.080002,191.839996,190.899994,191.330002,190.661331,12513900
74 | 2018-07-16,191.520004,192.649994,190.419998,190.910004,190.242798,15043100
75 | 2018-07-17,189.750000,191.869995,189.199997,191.449997,190.780899,15534500
76 | 2018-07-18,191.779999,191.800003,189.929993,190.399994,189.734573,16393400
77 | 2018-07-19,189.690002,192.550003,189.690002,191.880005,191.209412,20286800
78 | 2018-07-20,191.779999,192.429993,190.169998,191.440002,190.770950,20676200
79 | 2018-07-23,190.679993,191.960007,189.559998,191.610001,190.940353,15989400
80 | 2018-07-24,192.449997,193.660004,192.050003,193.000000,192.325485,18697900
81 | 2018-07-25,193.059998,194.850006,192.429993,194.820007,194.139145,16709900
82 | 2018-07-26,194.610001,195.960007,193.610001,194.210007,193.531265,19076000
83 | 2018-07-27,194.990005,195.190002,190.100006,190.979996,190.312546,24024000
84 | 2018-07-30,191.899994,192.199997,189.070007,189.910004,189.246292,21029500
85 | 2018-07-31,190.300003,192.139999,189.339996,190.289993,189.624954,39373000
86 | 2018-08-01,199.130005,201.759995,197.309998,201.500000,200.795792,67935700
87 | 2018-08-02,200.580002,208.380005,200.350006,207.389999,206.665207,62404000
88 | 2018-08-03,207.029999,208.740005,205.479996,207.990005,207.263107,33447400
89 | 2018-08-06,208.000000,209.250000,207.070007,209.070007,208.339340,25425400
90 | 2018-08-07,209.320007,209.500000,206.759995,207.110001,206.386185,25587400
91 | 2018-08-08,206.050003,207.809998,204.520004,207.250000,206.525696,22525500
92 | 2018-08-09,207.279999,209.779999,207.199997,208.880005,208.149994,23469200
93 | 2018-08-10,207.360001,209.100006,206.669998,207.529999,207.529999,24611200
94 | 2018-08-13,207.699997,210.949997,207.699997,208.869995,208.869995,25869100
95 | 2018-08-14,210.160004,210.559998,208.259995,209.750000,209.750000,20748000
96 | 2018-08-15,209.220001,210.740005,208.330002,210.240005,210.240005,28807600
97 | 2018-08-16,211.750000,213.809998,211.470001,213.320007,213.320007,28500400
98 | 2018-08-17,213.440002,217.949997,213.160004,217.580002,217.580002,35427000
99 | 2018-08-20,218.100006,219.179993,215.110001,215.460007,215.460007,30185300
100 | 2018-08-21,216.800003,217.190002,214.024994,215.039993,215.039993,25666584
101 |
--------------------------------------------------------------------------------
/data/^GSPC.csv:
--------------------------------------------------------------------------------
1 | Date,Open,High,Low,Close,Adj Close,Volume
2 | 2018-04-03,2592.169922,2619.139893,2575.489990,2614.449951,2614.449951,3392810000
3 | 2018-04-04,2584.040039,2649.860107,2573.610107,2644.689941,2644.689941,3350340000
4 | 2018-04-05,2657.360107,2672.080078,2649.580078,2662.840088,2662.840088,3178970000
5 | 2018-04-06,2645.820068,2656.879883,2586.270020,2604.469971,2604.469971,3299700000
6 | 2018-04-09,2617.179932,2653.550049,2610.790039,2613.159912,2613.159912,3062960000
7 | 2018-04-10,2638.409912,2665.449951,2635.780029,2656.870117,2656.870117,3543930000
8 | 2018-04-11,2643.889893,2661.429932,2639.250000,2642.189941,2642.189941,3020760000
9 | 2018-04-12,2653.830078,2674.719971,2653.830078,2663.989990,2663.989990,3021320000
10 | 2018-04-13,2676.899902,2680.260010,2645.050049,2656.300049,2656.300049,2960910000
11 | 2018-04-16,2670.100098,2686.489990,2665.159912,2677.840088,2677.840088,3019700000
12 | 2018-04-17,2692.739990,2713.340088,2692.050049,2706.389893,2706.389893,3234360000
13 | 2018-04-18,2710.110107,2717.489990,2703.629883,2708.639893,2708.639893,3383410000
14 | 2018-04-19,2701.159912,2702.840088,2681.899902,2693.129883,2693.129883,3349370000
15 | 2018-04-20,2692.560059,2693.939941,2660.610107,2670.139893,2670.139893,3388590000
16 | 2018-04-23,2675.399902,2682.860107,2657.989990,2670.290039,2670.290039,3017480000
17 | 2018-04-24,2680.800049,2683.550049,2617.320068,2634.560059,2634.560059,3706740000
18 | 2018-04-25,2634.919922,2645.300049,2612.669922,2639.399902,2639.399902,3499440000
19 | 2018-04-26,2651.649902,2676.479980,2647.159912,2666.939941,2666.939941,3665720000
20 | 2018-04-27,2675.469971,2677.350098,2659.010010,2669.909912,2669.909912,3219030000
21 | 2018-04-30,2682.510010,2682.870117,2648.040039,2648.050049,2648.050049,3734530000
22 | 2018-05-01,2642.959961,2655.270020,2625.409912,2654.800049,2654.800049,3559850000
23 | 2018-05-02,2654.239990,2660.870117,2631.699951,2635.669922,2635.669922,4010770000
24 | 2018-05-03,2628.080078,2637.139893,2594.620117,2629.729980,2629.729980,3851470000
25 | 2018-05-04,2621.449951,2670.929932,2615.320068,2663.419922,2663.419922,3327220000
26 | 2018-05-07,2680.340088,2683.350098,2664.699951,2672.629883,2672.629883,3237960000
27 | 2018-05-08,2670.260010,2676.340088,2655.199951,2671.919922,2671.919922,3717570000
28 | 2018-05-09,2678.120117,2701.270020,2674.139893,2697.790039,2697.790039,3909500000
29 | 2018-05-10,2705.020020,2726.110107,2704.540039,2723.070068,2723.070068,3333050000
30 | 2018-05-11,2722.699951,2732.860107,2717.449951,2727.719971,2727.719971,2862700000
31 | 2018-05-14,2738.469971,2742.100098,2725.469971,2730.129883,2730.129883,2972660000
32 | 2018-05-15,2718.590088,2718.590088,2701.909912,2711.449951,2711.449951,3290680000
33 | 2018-05-16,2712.620117,2727.760010,2712.169922,2722.459961,2722.459961,3202670000
34 | 2018-05-17,2719.709961,2731.959961,2711.360107,2720.129883,2720.129883,3475400000
35 | 2018-05-18,2717.350098,2719.500000,2709.179932,2712.969971,2712.969971,3368690000
36 | 2018-05-21,2735.389893,2739.189941,2725.699951,2733.010010,2733.010010,3019890000
37 | 2018-05-22,2738.340088,2742.239990,2721.879883,2724.439941,2724.439941,3366310000
38 | 2018-05-23,2713.979980,2733.330078,2709.540039,2733.290039,2733.290039,3326290000
39 | 2018-05-24,2730.939941,2731.969971,2707.379883,2727.760010,2727.760010,3256030000
40 | 2018-05-25,2723.600098,2727.360107,2714.989990,2721.330078,2721.330078,2995260000
41 | 2018-05-29,2705.110107,2710.669922,2676.810059,2689.860107,2689.860107,3736890000
42 | 2018-05-30,2702.429932,2729.340088,2702.429932,2724.010010,2724.010010,3561050000
43 | 2018-05-31,2720.979980,2722.500000,2700.679932,2705.270020,2705.270020,4235370000
44 | 2018-06-01,2718.699951,2736.929932,2718.699951,2734.620117,2734.620117,3684130000
45 | 2018-06-04,2741.669922,2749.159912,2740.540039,2746.870117,2746.870117,3376510000
46 | 2018-06-05,2748.459961,2752.610107,2739.510010,2748.800049,2748.800049,3517790000
47 | 2018-06-06,2753.250000,2772.389893,2748.459961,2772.350098,2772.350098,3651640000
48 | 2018-06-07,2774.840088,2779.899902,2760.159912,2770.370117,2770.370117,3711330000
49 | 2018-06-08,2765.840088,2779.389893,2763.590088,2779.030029,2779.030029,3123210000
50 | 2018-06-11,2780.179932,2790.209961,2780.169922,2782.000000,2782.000000,3232330000
51 | 2018-06-12,2785.600098,2789.800049,2778.780029,2786.850098,2786.850098,3401010000
52 | 2018-06-13,2787.939941,2791.469971,2774.649902,2775.629883,2775.629883,3779230000
53 | 2018-06-14,2783.209961,2789.060059,2776.520020,2782.489990,2782.489990,3526890000
54 | 2018-06-15,2777.780029,2782.810059,2761.729980,2779.659912,2779.659912,5428790000
55 | 2018-06-18,2765.790039,2774.989990,2757.120117,2773.750000,2773.750000,3287150000
56 | 2018-06-19,2752.010010,2765.050049,2743.189941,2762.590088,2762.590088,3661470000
57 | 2018-06-20,2769.729980,2774.860107,2763.909912,2767.320068,2767.320068,3327600000
58 | 2018-06-21,2769.280029,2769.280029,2744.389893,2749.760010,2749.760010,3300060000
59 | 2018-06-22,2760.790039,2764.169922,2752.679932,2754.879883,2754.879883,5450550000
60 | 2018-06-25,2742.939941,2742.939941,2698.669922,2717.070068,2717.070068,3655080000
61 | 2018-06-26,2722.120117,2732.909912,2715.600098,2723.060059,2723.060059,3555090000
62 | 2018-06-27,2728.449951,2746.090088,2699.379883,2699.629883,2699.629883,3776090000
63 | 2018-06-28,2698.689941,2724.340088,2691.989990,2716.310059,2716.310059,3428140000
64 | 2018-06-29,2727.129883,2743.260010,2718.030029,2718.370117,2718.370117,3565620000
65 | 2018-07-02,2704.949951,2727.260010,2698.949951,2726.709961,2726.709961,3073650000
66 | 2018-07-03,2733.270020,2736.580078,2711.159912,2713.219971,2713.219971,1911470000
67 | 2018-07-05,2724.189941,2737.830078,2716.020020,2736.610107,2736.610107,2953420000
68 | 2018-07-06,2737.679932,2764.409912,2733.520020,2759.820068,2759.820068,2554780000
69 | 2018-07-09,2775.620117,2784.649902,2770.729980,2784.169922,2784.169922,3050040000
70 | 2018-07-10,2788.560059,2795.580078,2786.239990,2793.840088,2793.840088,3063850000
71 | 2018-07-11,2779.820068,2785.909912,2770.770020,2774.020020,2774.020020,2964740000
72 | 2018-07-12,2783.139893,2799.219971,2781.530029,2798.290039,2798.290039,2821690000
73 | 2018-07-13,2796.929932,2804.530029,2791.689941,2801.310059,2801.310059,2614000000
74 | 2018-07-16,2797.360107,2801.189941,2793.389893,2798.429932,2798.429932,2812230000
75 | 2018-07-17,2789.340088,2814.189941,2789.239990,2809.550049,2809.550049,3050730000
76 | 2018-07-18,2811.350098,2816.760010,2805.889893,2815.620117,2815.620117,3089780000
77 | 2018-07-19,2809.370117,2812.050049,2799.770020,2804.489990,2804.489990,3266700000
78 | 2018-07-20,2804.550049,2809.699951,2800.010010,2801.830078,2801.830078,3230210000
79 | 2018-07-23,2799.169922,2808.610107,2795.139893,2806.979980,2806.979980,2907430000
80 | 2018-07-24,2820.679932,2829.989990,2811.120117,2820.399902,2820.399902,3417530000
81 | 2018-07-25,2817.729980,2848.030029,2817.729980,2846.070068,2846.070068,3553010000
82 | 2018-07-26,2835.489990,2845.570068,2835.260010,2837.439941,2837.439941,3653330000
83 | 2018-07-27,2842.350098,2843.169922,2808.340088,2818.820068,2818.820068,3415710000
84 | 2018-07-30,2819.000000,2821.739990,2798.110107,2802.600098,2802.600098,3245770000
85 | 2018-07-31,2809.729980,2824.459961,2808.060059,2816.290039,2816.290039,3892100000
86 | 2018-08-01,2821.169922,2825.830078,2805.850098,2813.360107,2813.360107,3496990000
87 | 2018-08-02,2800.479980,2829.909912,2796.340088,2827.219971,2827.219971,3467380000
88 | 2018-08-03,2829.620117,2840.379883,2827.370117,2840.350098,2840.350098,3030390000
89 | 2018-08-06,2840.290039,2853.290039,2835.979980,2850.399902,2850.399902,2874540000
90 | 2018-08-07,2855.919922,2863.429932,2855.919922,2858.449951,2858.449951,3162770000
91 | 2018-08-08,2856.790039,2862.439941,2853.090088,2857.699951,2857.699951,2972200000
92 | 2018-08-09,2857.189941,2862.479980,2851.979980,2853.580078,2853.580078,3047050000
93 | 2018-08-10,2838.899902,2842.199951,2825.810059,2833.280029,2833.280029,3256040000
94 | 2018-08-13,2835.459961,2843.399902,2819.879883,2821.929932,2821.929932,3158450000
95 | 2018-08-14,2827.879883,2843.110107,2826.580078,2839.959961,2839.959961,2976970000
96 | 2018-08-15,2827.949951,2827.949951,2802.489990,2818.370117,2818.370117,3645070000
97 | 2018-08-16,2831.439941,2850.489990,2831.439941,2840.689941,2840.689941,3219880000
98 | 2018-08-17,2838.320068,2855.629883,2833.729980,2850.129883,2850.129883,3024100000
99 | 2018-08-20,2853.929932,2859.760010,2850.620117,2857.050049,2857.050049,2748020000
100 | 2018-08-21,2861.510010,2873.229980,2861.320068,2862.959961,2862.959961,1897094700
101 |
--------------------------------------------------------------------------------
/src/Portfolio.py:
--------------------------------------------------------------------------------
1 | """ Computes a Portfolio's Cumulative Return, Average Daily Returns, Risk, and Sharpe Ratio.
2 |
3 | Optimizes a portfolio using stock covariance for volatility minimization
4 | and cumulative return maximization with stochastic gradient descent
5 | to minimize the negative Sharpe Ratio function for various X parameters
6 | representing the different percentage allocations for each Stock.
7 |
8 | Note that this minimizes the Sharpe Ratio, not cumulative returns.
9 | This is because optimizing for cumulative returns is trivial;
10 | merely invest 100% in the one stock that has increased the most!
11 | Instead, this minimizes risk as well, which also makes it much more useful
12 | for the future since securities tend to maintain the same levels of volatility.
13 |
14 | Imagine two companies competing in the same sector. Perhaps one company does better,
15 | the other tends to do worse. This is a negative covariance.
16 | In this case, it would be possible to attain the returns of both of the stocks
17 | with nearly zero risk, as the volatilities of each company become cancelled out
18 | if allocations are set evenly to 50% and 50%.
19 | This is the big picture of how this optimizes for risk mitigation in addition to cumulative returns.
20 |
21 | See 'Examples.py' for example usage.
22 |
23 | """
24 |
25 |
26 | import pandas as pd
27 | import numpy as np
28 | import datetime as dt
29 | import matplotlib.pyplot as plt
30 | import scipy.optimize as spo
31 | import time
32 |
33 | # local
34 | from src import QuoteHistory
35 |
36 |
37 |
38 | class Portfolio(object):
39 |
40 | def __init__(self, start_value, symbols, allocs, start_date=None, end_date=None, fresh=False, days=None, df=None):
41 | """start_value: starting value of portfolio, like 1000.50
42 | allocs: percentage allocations for each stock, like [0.3, 0.4, 0.3]
43 | start_date, end_date: like '2018-12-13'
44 | fresh: if True, will fetch entirely new data for each stock
45 | df: dataframe used ONLY within optimizer function below.
46 | """
47 | self.start_value = start_value
48 | self.start_date = start_date
49 | self.end_date = end_date
50 | self.allocs = allocs
51 | self.fresh = fresh
52 | self.symbols = symbols
53 | self.days = days
54 | if df is None:
55 | df = self.get_data(symbols)
56 | self.df = df
57 | self.port_val = self.get_port_val(df, allocs, start_value)
58 | self.daily_returns = self.daily_returns(self.port_val)
59 |
60 | def get_data(self, symbols):
61 | """Read stock data (adjusted close) for given symbols from CSV files.
62 | Returns dataframe of all stocks inner joined on ^GSPC's 'Date' index_col
63 | """
64 | # set up dataframe with S&P 500
65 | df = QuoteHistory.get_data('^IXIC', self.fresh, self.start_date, self.end_date, self.days)
66 | df = df.rename(columns={'Adj Close': '^IXIC'}).drop('Volume', axis=1).dropna()
67 | for symbol in symbols:
68 | try:
69 | df_temp = QuoteHistory.get_data(symbol, self.fresh, self.start_date, self.end_date, self.days)
70 | df_temp = df_temp.drop('Volume', axis=1) # bc we only want adjusted close
71 | df[symbol] = df_temp # adds symbol to DF
72 | except Exception:
73 | print('Failed to determine for {}'.format(symbol))
74 |
75 | df = df.drop('^IXIC', axis=1) # gets rid of first stock used for indexing
76 | # deal with NaN's by filling existing values forward, then backward
77 | # ideally, user should just use new data!
78 | df = df.fillna(method='ffill').fillna(method='backfill')
79 | return df
80 |
81 |
82 |
83 | def debug(self):
84 | """ Prints out the analysis of the portfolio """
85 | # Optimize the portfolio
86 | x, y = self.optimizer()
87 | optimized = Portfolio(1.0, self.symbols, x, self.start_date, self.end_date, self.fresh, self.days, self.df)
88 |
89 | print('Minima found at\nx = {}, y = {}'.format(x, y))
90 | print("\nOptimized Allocations for Maxmimum Risk-Adjusted Returns:")
91 |
92 | # sort X and its accompanying symbols, descending
93 | sorted_indices = x.argsort()
94 | x = x[sorted_indices[::-1]]
95 | symbols = np.array(self.symbols)
96 | symbols = symbols[sorted_indices[::-1]]
97 |
98 | # print out each symbol with its percentage allocation
99 | for i in range(0, len(x)):
100 | symbol = symbols[i]
101 | percent = round((x[i] * 100), 2)
102 | print('{}: {}%'.format(symbol, percent))
103 |
104 | # print stats for before and after
105 | print("\nBefore optimization:")
106 | self.print_stats(self)
107 | print("\nAfter optimization:")
108 | self.print_stats(optimized)
109 |
110 | # plot before and after
111 | ax = self.port_val.plot(title='Portfolio Optimization Results', label='Before')
112 | optimized.port_val.plot(label='After')
113 | ax.set_xlabel("Date")
114 | ax.set_ylabel("Normalized Total Value")
115 | ax.legend(loc='best')
116 | plt.show()
117 |
118 | def print_stats(self, portfolio):
119 | """ prints out a portfolio's stats. called from self.debug() """
120 | sharpe = round(portfolio.sharpe_ratio(portfolio.daily_returns), 2)
121 | volatility = round(portfolio.risk(), 5)
122 | dr = round(portfolio.avg_daily_returns() * 100, 4)
123 | cr = round(portfolio.cumulative_return() * 100, 2)
124 | print("Sharpe Ratio: {}".format(sharpe))
125 | print("Volatility: {}".format(volatility))
126 | print("Average Daily Returns: {}%".format(dr))
127 | print("Cumulative Returns: {}%".format(cr))
128 |
129 |
130 |
131 | def get_port_val(self, df, allocs, start_value):
132 | """ returns dataframe of portfolio's total value day-by-day """
133 | df /= df.iloc[0]
134 | df *= allocs
135 | df *= start_value
136 | df = df.sum(axis = 1)
137 | return df
138 |
139 |
140 | def daily_returns(self, df):
141 | """ returns daily returns df,
142 | where 0.01 on a specific day represents 1% gained on that day
143 | """
144 | returns = df.copy()
145 | returns[1:] = (df[1:] / df[:-1].values) - 1
146 | returns.ix[0] = 0
147 | return returns
148 |
149 |
150 | def cumulative_return(self):
151 | """ return on the portfolio from start to finish,
152 | like 1.5 representing +50% in returns
153 | """
154 | return self.port_val[-1] / self.port_val[0] - 1
155 |
156 |
157 | def avg_daily_returns(self):
158 | """ average of daily returns """
159 | return self.daily_returns.mean()
160 |
161 |
162 | def risk(self):
163 | """ volatility of a stock measured by standard deviation """
164 | return self.daily_returns.std()
165 |
166 |
167 | def sharpe_ratio(self, daily_returns):
168 | """
169 | the annualized risk-adjusted return.
170 | assumes daily frequency, and an annual risk-free amount of 2%.
171 | Sharpe Ratio = sqrt(252) * mean(daily returns - daily risk free)
172 | -------------------------------------------------
173 | stddev(daily returns)
174 | SR should be around 0.5-3.
175 | Less than 1 is risky, 1-3 is good, more than 3 is amazing.
176 | """
177 | # daily_risk_free = 1.02 ** (1. / 252) - 1 # 2 percent annual, current T-Bill
178 | sqrt = np.sqrt(252)
179 | mean = daily_returns.mean()
180 | stddev = daily_returns.std()
181 | return sqrt * mean / stddev
182 |
183 |
184 | def ending_value(self):
185 | return self.port_val[-1]
186 |
187 |
188 | def sharpe_optimizer(self, allocs):
189 | """ function being minimized
190 | Takes `allocs`, a list of percentage allocations like [0.6, 0.4] as input
191 | to determine gradient descent of the sharpe ratio function
192 | """
193 | daily_returns = Portfolio(1.0, self.symbols, allocs, self.start_date, self.end_date, self.fresh, self.days, self.df).daily_returns
194 |
195 | # get sharpe ratio with new allocations
196 | y = self.sharpe_ratio(daily_returns)
197 |
198 | # Example: x = [0.06613882 0.93386119 0.000000], y = 3.5938127680415937
199 | print("x = {}, y = {}".format(allocs, y))
200 |
201 | # negative because MINIMIZING a NEGATIVE sharpe is the same as maximizing it
202 | return y * -1
203 |
204 |
205 | def optimizer(self):
206 | """ Minimizes the sharpe_optimizer() function using gradient descent.
207 | """
208 | Xguess = self.allocs # initial guess for X
209 | bounds = len(self.symbols) * [(0, 1)] # keep allocations between 0 and 1
210 | # constrains sum to equal 1.0 (100%)
211 | min_result = spo.minimize(self.sharpe_optimizer, Xguess, method='SLSQP',\
212 | bounds = bounds,\
213 | constraints = ({ 'type': 'eq', 'fun': lambda inputs: 1.0 - np.sum(inputs) }),\
214 | options={'disp':True})
215 |
216 | return min_result.x, min_result.fun
217 |
--------------------------------------------------------------------------------
/src/Stock.py:
--------------------------------------------------------------------------------
1 | """ Models a Stock with various indicators,
2 | with the primary feature of deciding whether to Buy, Sell, or Hold a Stock.
3 |
4 | See 'Examples.py' for example usage.
5 |
6 | TODO:
7 | Set up automatic trading
8 | Show date range in plot title
9 | """
10 |
11 |
12 | import matplotlib.pyplot as plt
13 | import pandas as pd
14 | import numpy as np
15 | from sklearn import linear_model
16 | from sklearn.neighbors import KNeighborsRegressor
17 | import warnings
18 |
19 | # local
20 | from src import QuoteHistory
21 |
22 |
23 | class Stock(object):
24 | """ Evaluates a given Stock with various indicators
25 | and suggests to buy, sell, or hold using technical analysis.
26 | Technical analysis assumes the price represents all information, public and private.
27 | Note that technical analysis may not be enough to make a decision;
28 | fundamental analysis of a company's earnings, dividends,
29 | balance sheet, growth potential, and common sense are necessary.
30 | """
31 |
32 |
33 | # SETUP
34 | #
35 | def __init__(self, symbol, shares, avg_paid, fresh=False, fresh_sp=False, plot=True, days=140):
36 | """ Sample Inputs:
37 | Symbol 'SBUX', Current Shares 0, Avg Price Paid 50.10
38 | Fresh = True fetches new data
39 | """
40 | self.buys = 0
41 | self.sells = 0
42 | self.shares = shares
43 | self.symbol = symbol
44 | self.will_plot = plot
45 | self.daily = QuoteHistory.get_data(symbol, fresh, days=days)
46 | self.sp = QuoteHistory.get_data('^IXIC', fresh_sp, days=days) # now the nasdaq
47 | warnings.filterwarnings(action="ignore", module="sklearn", message="internal gelsd")
48 | self.avg_paid = avg_paid
49 | self.debug = '\n' + self.symbol + ':\n'
50 |
51 |
52 |
53 | def buy_or_sell(self, debug=True):
54 | """ Determines whether to Buy, Sell, or Hold
55 | along with an explanation and several charts.
56 |
57 | If an indicator supports a Buy, increment self.Buys, and vice versa.
58 | I will then subtract Sell points from Buy points to see the certainty in either direction.
59 | """
60 | if self.will_plot:
61 | self.plot()
62 | self.predict()
63 | self.check_sma()
64 | self.check_bollinger()
65 | self.volume()
66 | self.rsi()
67 | self.sharpe()
68 | self.extrema()
69 | self.net_gains()
70 | self.beta_and_alpha()
71 | self.decision()
72 | if debug: print(self.debug)
73 | if self.will_plot:
74 | plt.show()
75 |
76 |
77 | def decision(self):
78 | self.debug += '\nBuying Points: {}'.format(self.buys)
79 | self.debug += '\nSelling Points: {}'.format(self.sells)
80 | if self.buys > self.sells and self.sells * 2 < self.buys:
81 | decision = 'BUY'
82 | elif self.sells > self.buys and self.buys * 2 < self.sells:
83 | decision = 'SELL'
84 | else:
85 | decision = 'HOLD'
86 | self.debug += '\nFINAL DECISION: \n{}'.format(decision)
87 | # certainty to just BUY, used for s&p traversals.
88 | self.buying_certainty = self.buys - self.sells
89 | # percent certainty for a sell or buy
90 | # is difference divided by sum of sell and buys.
91 | # for example, certainty of 1 sell/9 buys =
92 | # (abs(1-9)) / (1 + 9) = 8 / 10 = 0.80 = 80% certainty (to sell, in this case)
93 | percent_certainty = abs(self.buys - self.sells) / (self.buys + self.sells)
94 | if decision == 'HOLD':
95 | percent_certainty = 1 - percent_certainty
96 | percent_certainty = round(percent_certainty * 100, 2)
97 | self.debug += '\nCertainty: {}%'.format(percent_certainty)
98 |
99 |
100 | def plot(self):
101 | # Adjusted Close
102 | Adj_Close = self.normalize(self.daily['Adj Close'])
103 | ax = Adj_Close.plot(title=self.symbol, label=self.symbol, color='cyan', linewidth=5)
104 | self.ax = ax # used by other plots like lin reg
105 | # Simple Moving Average
106 | self.sma(normalize=True).plot(label="SMA", alpha=0.5)
107 |
108 | # Bollinger Bands
109 | upper, lower = self.bollinger_bands(normalize=True)
110 | upper.plot(label='upper', ax=ax, alpha=0.5)
111 | lower.plot(label='lower', ax=ax, alpha=0.5)
112 |
113 | # S&P 500 (the market)
114 | sp_normalized = self.normalize(self.sp['Adj Close'])
115 | sp_normalized.plot(label='S&P 500', alpha=0.5)
116 |
117 | # Add axis labels and legend
118 | ax.set_xlabel("Date")
119 | ax.set_ylabel("Normalized Price")
120 | ax.legend(loc='upper left')
121 |
122 | # Plot Volume on a different Range
123 | ax2 = ax.twinx()
124 | ax2.plot(self.daily['Volume'], color='gray', alpha=0.3)
125 | ax2.set_ylabel("Volume")
126 | ax2.legend(loc='lower right')
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | # HELPERS
141 | #
142 | def sma(self, normalize=False, window=20):
143 | """ Simple Moving Average
144 | Determines the simple moving average, with a 20-day window by default.
145 | Returns the SMA as a
146 | """
147 | adj_close = self.daily['Adj Close']
148 | if normalize: adj_close = self.normalize(adj_close)
149 | sma = adj_close.rolling(window).mean()
150 | return sma
151 |
152 | def ema(self, normalize=False, span=20):
153 | """ exponential moving average """
154 | values = self.daily['Adj Close']
155 | if normalize: values = self.normalize(values)
156 | return pd.Series.ewm(values, span=span).mean()
157 |
158 | def rolling_std(self, window=20, normalize=False):
159 | """Returns rolling standard deviation of given values, using specified window size."""
160 | values = self.daily['Adj Close']
161 | if normalize: values = self.normalize(values)
162 | return values.rolling(window).std()
163 |
164 | def bollinger_bands(self, normalize=False):
165 | """ Bollinger Bands
166 | Given SMA, returns the Series of upper and lower Bollinger Bands
167 | """
168 | sma = self.sma(normalize=normalize)
169 | rstd = self.rolling_std(normalize=normalize)
170 | upper_band = sma + rstd
171 | lower_band = sma - rstd
172 | return upper_band, lower_band
173 |
174 | def normalize(self, df):
175 | """normalizes data by dividing by first row"""
176 | return df / df.ix[0, :]
177 |
178 | def daily_returns(self, df):
179 | """Compute and return the daily return values."""
180 | daily_returns = df.copy()
181 | daily_returns[1:] = (df[1:] / df[:-1].values) - 1
182 | daily_returns.ix[0] = 0
183 | return daily_returns
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | # INDICATORS
194 | #
195 | def predict(self):
196 | """Predicts tomorrow's price using ML algorithms KNN and Linear Regression
197 | """
198 | # format data
199 | df = self.normalize(self.daily)
200 | x = df.index.astype(np.int64).values.reshape(-1, 1)
201 | y = self.normalize(df[['Adj Close']]).values
202 |
203 | # format time
204 | one_day_time = 86400000000000
205 | x_tomorrow = x[-1] + one_day_time
206 | x_incl_tomorrow = np.append(x, [x_tomorrow], axis=0)
207 | dates = pd.to_datetime(x_incl_tomorrow.reshape(-1))
208 |
209 | # average the predictions
210 | lin_reg = self.linear_regression(x, y, x_tomorrow, x_incl_tomorrow, dates)
211 | knn = self.knn(x, y, x_tomorrow, x_incl_tomorrow, dates)
212 | tomorrow_norm = [(lin_reg + knn) / 2]
213 | today_norm = [df['Adj Close'][-1]]
214 | tomorrow = round((tomorrow_norm[0] * self.daily['Adj Close'][0]), 2)
215 | today = self.daily['Adj Close'][-1]
216 | percent_gain = round((((tomorrow / today) - 1) * 100), 2)
217 | percent_gain_int = abs(int(round(percent_gain, 0)))
218 |
219 | if percent_gain > 0:
220 | self.debug += '\nExpected price gain: {} %, buys + {}, predicted close is {}'.format(percent_gain, percent_gain_int, tomorrow)
221 | self.buys += percent_gain_int
222 | else:
223 | self.debug += '\nExpected price gain: {} %, sells + {}, predicted close is {}'.format(percent_gain, percent_gain_int, tomorrow)
224 | self.sells += percent_gain_int
225 |
226 | # plots dotted line connecting stock today with tomorrow's prediction
227 | predicting_line = np.append(today_norm, tomorrow_norm, axis=0)
228 |
229 | if self.will_plot:
230 | self.ax.plot(dates[-2:], predicting_line, color='cyan', dashes=([1, 1, 1, 1]))
231 | self.ax.plot(pd.to_datetime(x_tomorrow), tomorrow_norm, marker='o', markersize=3, color="cyan")
232 |
233 |
234 | def linear_regression(self, x, y, x_tomorrow, x_incl_tomorrow, dates):
235 | """ Checks Trend
236 | determines whether Linearly Regressing up, down or flat within time period.
237 | """
238 | regr = linear_model.LinearRegression()
239 | regr.fit(x, y)
240 |
241 | # Get Coefficients
242 | m = regr.coef_[0]
243 | b = regr.intercept_
244 |
245 | tomorrow_normalized = regr.predict([x_tomorrow])[0][0]
246 | tomorrow = round((tomorrow_normalized * self.daily['Adj Close'][0]), 2)
247 | today = self.daily['Adj Close'][-1]
248 | percent_gain = round(((tomorrow / today) - 1) * 100, 2)
249 | percent_gain_int = abs(int(round(percent_gain, 0)))
250 |
251 | # plot Lin Reg
252 | if self.will_plot:
253 | self.ax.plot(dates, regr.predict(x_incl_tomorrow), color='k', label='Linear Regression', dashes=([2, 2, 10, 2]))
254 | self.ax.legend(loc='best')
255 |
256 | # update points
257 | if m > 0:
258 | # trend is upwards
259 | self.debug += '\nPositive Regression Trend: buys + 3'
260 | self.buys += 3
261 | else:
262 | self.debug += '\nNegative Regression Trend: sells + 3'
263 | self.sells += 3
264 |
265 | if percent_gain > 0:
266 | self.debug += '\nRegression: Price will be up {} %, predicted close is {}'.format(percent_gain, tomorrow)
267 | else:
268 | self.debug += '\nRegression: Price will be down {} %, predicted close is {}'.format(percent_gain, tomorrow)
269 |
270 | return tomorrow_normalized
271 |
272 |
273 | def knn(self, x, y, x_tomorrow, x_incl_tomorrow, dates):
274 | """ predicts future price using knn """
275 | neigh = KNeighborsRegressor(n_neighbors=5)
276 | neigh.fit(x, y)
277 |
278 | tomorrow_normalized = neigh.predict([x_tomorrow])[0][0]
279 | tomorrow = round((tomorrow_normalized * self.daily['Adj Close'][0]), 2)
280 | today = self.daily['Adj Close'][-1]
281 | percent_gain = round(((tomorrow / today) - 1) * 100, 2)
282 | percent_gain_int = abs(int(round(percent_gain, 0)))
283 |
284 | if self.will_plot:
285 | self.ax.plot(dates, neigh.predict(x_incl_tomorrow),label='KNN', dashes=([1, 1, 5, 1]))
286 | self.ax.legend(loc='best')
287 |
288 | if percent_gain > 0:
289 | self.debug += '\nKNN: Price will be up {} %, predicted close is {}'.format(percent_gain, tomorrow)
290 | else:
291 | self.debug += '\nKNN: Price will be down {} %, predicted close is {}'.format(percent_gain, tomorrow)
292 |
293 | return tomorrow_normalized
294 |
295 |
296 | def check_sma(self):
297 | """ Checks Simple Moving Average
298 | """
299 | sma = self.sma()
300 | if self.daily['Adj Close'][-1] < sma[-1]:
301 | self.debug += '\nBelow SMA: buys + 1'
302 | self.buys += 1
303 | else:
304 | self.debug += '\nAbove SMA: sells + 1'
305 | self.sells += 1
306 |
307 | def check_bollinger(self):
308 | """ Checks Bollinger Bands
309 | Checks for crossovers INTO a band, or the current price's relation to the bands.
310 | """
311 | upper, lower = self.bollinger_bands()
312 | if self.daily['Adj Close'][-1] > upper[-1]:
313 | self.debug += '\nAbove upper bollinger: sells + 1'
314 | self.sells += 1
315 | elif self.daily['Adj Close'][-1] < lower[-1]:
316 | self.debug += '\nBelow lower bollinger: buys + 1'
317 | self.buys += 1
318 |
319 | def volume(self):
320 | """ Volume
321 | Determines whether volume has been trending up or down recently.
322 | Higher volume supports a trend.
323 | """
324 | vol = self.daily['Volume']
325 | sma = vol.rolling(20).mean()
326 | std = vol.rolling(20).std()
327 | upper = sma + std
328 | lower = sma - std
329 |
330 | if vol[-1] > upper[-1]:
331 | self.debug += '\nVolume > 1 STD above sma: buys + 1 and sells + 1'
332 | self.sells += 1
333 | self.buys += 1
334 | else:
335 | self.debug += '\nVolume in normal levels'
336 |
337 | def rsi(self, days=14):
338 | """ relevant strength index oscillator
339 | """
340 | daily_returns = self.daily_returns(self.daily[['Adj Close']]).tail(14)
341 | green_days = daily_returns[daily_returns > 0].dropna()
342 | red_days = daily_returns[daily_returns < 0].dropna()
343 |
344 | # RS= avg of Up Days - avg of Down Days. Note im adding bc red_days is negative.
345 | RS = (green_days.sum() / days + red_days.sum() / days)['Adj Close']
346 | rsi = 100 - (100 / (1 + RS))
347 | rsi = round(rsi, 2)
348 | if rsi < 0.3:
349 | self.debug += '\nRSI is under 30%: {0} buys + 1'.format(rsi)
350 | self.buys += 1
351 | elif rsi > 0.7:
352 | self.debug += '\nRSI over 70%: {0} sells + 1'.format(rsi)
353 | self.sells += 1
354 | else:
355 | self.debug += '\nRSI is normal(between 0.3-0.7): {}'.format(rsi)
356 |
357 |
358 | def sharpe(self):
359 | """ sharpe ratio
360 | """
361 | annual_free = 1.02 # 2 percent annual
362 | daily_risk_free = annual_free ** (1. / 252) - 1
363 | sqrt = np.sqrt(252)
364 | dr = self.daily_returns(self.daily['Adj Close'])
365 | mean = (dr - daily_risk_free).mean()
366 | stddev = dr.std()
367 | sharpe = sqrt * mean / stddev
368 | sharpe = round(sharpe, 1)
369 | rounded = int(round(sharpe, 0))
370 |
371 | if sharpe < 1:
372 | self.debug += '\nSharpe Ratio too low: {} sells + 1'.format(sharpe)
373 | self.sells += 1
374 | elif sharpe < 2:
375 | self.debug += '\nSharpe Ratio is {}, buys + 1'.format(sharpe)
376 | self.buys += 1
377 | elif sharpe < 3:
378 | self.debug += '\nGood Sharpe Ratio of {}, buys + 2'.format(sharpe)
379 | self.buys += 2
380 | else:
381 | self.debug += '\nVery good Sharpe Ratio of {0}, buys + 3'.format(sharpe)
382 | self.buys += 3
383 |
384 | def extrema(self):
385 | """ checks for extrema: 100 day high/low
386 | """
387 | df = self.daily['Adj Close']
388 | min = df.min()
389 | max = df.max()
390 | if df[-1] == min:
391 | self.debug += '\nLocal Minimum: Buys + 1'
392 | self.buys += 1
393 | elif df[-1] == max:
394 | self.debug += '\nLocal Maximum: Sells + 1'
395 | self.sells += 1
396 | else:
397 | self.debug += '\nNot a local extrema.'
398 |
399 | def net_gains(self):
400 | """ Net Gains from this stock, if i have been holding.
401 | This is very important!
402 | "The first rule is to never lose. And the second rule is to never
403 | forget about that first rule." - Warren Buffett
404 | """
405 | if self.shares == 0:
406 | self.debug += '\nNo shares owned.'
407 | else:
408 | price = self.daily['Adj Close'][-1]
409 | gains = self.shares * (price - self.avg_paid)
410 | percent = (price / self.avg_paid - 1) * 100
411 | gains = round(gains, 2)
412 | percent = round(percent, 2)
413 | if gains < 0:
414 | penalty = self.sells - int(round(self.sells / 3.0, 0))
415 | self.debug += '\nNET LOSS: {}, {}%, AVOID SELLING! sells - {}'.format(gains, percent, penalty)
416 | self.sells -= penalty
417 | else:
418 | self.debug += '\nNet gains: ${}, {}%'.format(gains, percent)
419 |
420 |
421 | def beta_and_alpha(self):
422 | """ checks the Beta and Alpha of the stock with the market (sp500) '
423 |
424 | Also charts scatter plot.
425 | """
426 | # make scatter plot
427 | sp_temp = self.daily_returns(self.sp.rename(columns={'Adj Close': '^GSPC'}))
428 | symbol_temp = self.daily_returns(self.daily.rename(columns={'Adj Close': self.symbol}))
429 | joined = sp_temp.merge(symbol_temp, on='Date')
430 |
431 | # beta and alpha
432 | beta, alpha = np.polyfit(joined["^GSPC"], joined[self.symbol], 1)
433 | beta = round(beta, 3)
434 | alpha = round(alpha, 5)
435 | if alpha > 0:
436 | self.buys += 1
437 | self.debug += '\nAlpha > 0: buys + {}'.format(alpha)
438 | else:
439 | self.debug += '\nAlpha < 0: {}'.format(alpha)
440 |
441 | # assuming favorable market conditions. else, it would be sells + 1.
442 | if beta > 1:
443 | self.buys += 1
444 | self.debug += '\nBeta > 1: buys + {}'.format(beta)
445 | else:
446 | self.debug += '\nBeta < 1: {}'.format(beta)
447 |
448 | # finish plotting scatter
449 | if self.will_plot:
450 | ax = joined.plot(title=self.symbol + ' vs The Market', kind = 'scatter', x='^GSPC', y=self.symbol)
451 | ax.set_xlabel("S&P 500")
452 | plt.plot(joined["^GSPC"], beta * joined['^GSPC'] + alpha, '-', color='r', label='Correlation')
453 |
454 | # plot expected beta (slope) of 1 and alpha (y- int.) of zero
455 | plt.plot(joined["^GSPC"], 1 * joined['^GSPC'] + 0, '-', color='gray', label='Beta of 1')
456 | plt.plot(joined["^GSPC"], 0 * joined['^GSPC'] + 0, '-', color='gray', label='Alpha of 0')
457 | plt.legend(loc='best')
458 |
--------------------------------------------------------------------------------
/indices/sp_members.csv:
--------------------------------------------------------------------------------
1 | Symbol,Name,Sector
2 | MMM,3M Company,Industrials
3 | AOS,A.O. Smith Corp,Industrials
4 | ABT,Abbott Laboratories,Health Care
5 | ABBV,AbbVie Inc.,Health Care
6 | ACN,Accenture plc,Information Technology
7 | ATVI,Activision Blizzard,Information Technology
8 | AYI,Acuity Brands Inc,Industrials
9 | ADBE,Adobe Systems Inc,Information Technology
10 | AAP,Advance Auto Parts,Consumer Discretionary
11 | AMD,Advanced Micro Devices Inc,Information Technology
12 | AES,AES Corp,Utilities
13 | AET,Aetna Inc,Health Care
14 | AMG,Affiliated Managers Group Inc,Financials
15 | AFL,AFLAC Inc,Financials
16 | A,Agilent Technologies Inc,Health Care
17 | APD,Air Products & Chemicals Inc,Materials
18 | AKAM,Akamai Technologies Inc,Information Technology
19 | ALK,Alaska Air Group Inc,Industrials
20 | ALB,Albemarle Corp,Materials
21 | ARE,Alexandria Real Estate Equities Inc,Real Estate
22 | ALXN,Alexion Pharmaceuticals,Health Care
23 | ALGN,Align Technology,Health Care
24 | ALLE,Allegion,Industrials
25 | AGN,"Allergan, Plc",Health Care
26 | ADS,Alliance Data Systems,Information Technology
27 | LNT,Alliant Energy Corp,Utilities
28 | ALL,Allstate Corp,Financials
29 | GOOGL,Alphabet Inc Class A,Information Technology
30 | GOOG,Alphabet Inc Class C,Information Technology
31 | MO,Altria Group Inc,Consumer Staples
32 | AMZN,Amazon.com Inc.,Consumer Discretionary
33 | AEE,Ameren Corp,Utilities
34 | AAL,American Airlines Group,Industrials
35 | AEP,American Electric Power,Utilities
36 | AXP,American Express Co,Financials
37 | AIG,"American International Group, Inc.",Financials
38 | AMT,American Tower Corp A,Real Estate
39 | AWK,American Water Works Company Inc,Utilities
40 | AMP,Ameriprise Financial,Financials
41 | ABC,AmerisourceBergen Corp,Health Care
42 | AME,AMETEK Inc.,Industrials
43 | AMGN,Amgen Inc.,Health Care
44 | APH,Amphenol Corp,Information Technology
45 | APC,Anadarko Petroleum Corp,Energy
46 | ADI,"Analog Devices, Inc.",Information Technology
47 | ANDV,Andeavor,Energy
48 | ANSS,ANSYS,Information Technology
49 | ANTM,Anthem Inc.,Health Care
50 | AON,Aon plc,Financials
51 | APA,Apache Corporation,Energy
52 | AIV,Apartment Investment & Management,Real Estate
53 | AAPL,Apple Inc.,Information Technology
54 | AMAT,Applied Materials Inc.,Information Technology
55 | APTV,Aptiv Plc,Consumer Discretionary
56 | ADM,Archer-Daniels-Midland Co,Consumer Staples
57 | ARNC,Arconic Inc.,Industrials
58 | AJG,Arthur J. Gallagher & Co.,Financials
59 | AIZ,Assurant Inc.,Financials
60 | T,AT&T Inc.,Telecommunication Services
61 | ADSK,Autodesk Inc.,Information Technology
62 | ADP,Automatic Data Processing,Information Technology
63 | AZO,AutoZone Inc,Consumer Discretionary
64 | AVB,"AvalonBay Communities, Inc.",Real Estate
65 | AVY,Avery Dennison Corp,Materials
66 | BHGE,"Baker Hughes, a GE Company",Energy
67 | BLL,Ball Corp,Materials
68 | BAC,Bank of America Corp,Financials
69 | BAX,Baxter International Inc.,Health Care
70 | BBT,BB&T Corporation,Financials
71 | BDX,Becton Dickinson,Health Care
72 | BRK.B,Berkshire Hathaway,Financials
73 | BBY,Best Buy Co. Inc.,Consumer Discretionary
74 | BIIB,Biogen Inc.,Health Care
75 | BLK,BlackRock,Financials
76 | HRB,Block H&R,Financials
77 | BA,Boeing Company,Industrials
78 | BKNG,Booking Holdings Inc,Consumer Discretionary
79 | BWA,BorgWarner,Consumer Discretionary
80 | BXP,Boston Properties,Real Estate
81 | BSX,Boston Scientific,Health Care
82 | BHF,Brighthouse Financial Inc,Financials
83 | BMY,Bristol-Myers Squibb,Health Care
84 | AVGO,Broadcom,Information Technology
85 | BF.B,Brown-Forman Corp.,Consumer Staples
86 | CHRW,C. H. Robinson Worldwide,Industrials
87 | CA,"CA, Inc.",Information Technology
88 | COG,Cabot Oil & Gas,Energy
89 | CDNS,Cadence Design Systems,Information Technology
90 | CPB,Campbell Soup,Consumer Staples
91 | COF,Capital One Financial,Financials
92 | CAH,Cardinal Health Inc.,Health Care
93 | KMX,Carmax Inc,Consumer Discretionary
94 | CCL,Carnival Corp.,Consumer Discretionary
95 | CAT,Caterpillar Inc.,Industrials
96 | CBOE,Cboe Global Markets,Financials
97 | CBRE,CBRE Group,Real Estate
98 | CBS,CBS Corp.,Consumer Discretionary
99 | CELG,Celgene Corp.,Health Care
100 | CNC,Centene Corporation,Health Care
101 | CNP,CenterPoint Energy,Utilities
102 | CTL,CenturyLink Inc,Telecommunication Services
103 | CERN,Cerner,Health Care
104 | CF,CF Industries Holdings Inc,Materials
105 | SCHW,Charles Schwab Corporation,Financials
106 | CHTR,Charter Communications,Consumer Discretionary
107 | CVX,Chevron Corp.,Energy
108 | CMG,Chipotle Mexican Grill,Consumer Discretionary
109 | CB,Chubb Limited,Financials
110 | CHD,Church & Dwight,Consumer Staples
111 | CI,CIGNA Corp.,Health Care
112 | XEC,Cimarex Energy,Energy
113 | CINF,Cincinnati Financial,Financials
114 | CTAS,Cintas Corporation,Industrials
115 | CSCO,Cisco Systems,Information Technology
116 | C,Citigroup Inc.,Financials
117 | CFG,Citizens Financial Group,Financials
118 | CTXS,Citrix Systems,Information Technology
119 | CME,CME Group Inc.,Financials
120 | CMS,CMS Energy,Utilities
121 | KO,Coca-Cola Company (The),Consumer Staples
122 | CTSH,Cognizant Technology Solutions,Information Technology
123 | CL,Colgate-Palmolive,Consumer Staples
124 | CMCSA,Comcast Corp.,Consumer Discretionary
125 | CMA,Comerica Inc.,Financials
126 | CAG,Conagra Brands,Consumer Staples
127 | CXO,Concho Resources,Energy
128 | COP,ConocoPhillips,Energy
129 | ED,Consolidated Edison,Utilities
130 | STZ,Constellation Brands,Consumer Staples
131 | GLW,Corning Inc.,Information Technology
132 | COST,Costco Wholesale Corp.,Consumer Staples
133 | COTY,"Coty, Inc",Consumer Staples
134 | CCI,Crown Castle International Corp.,Real Estate
135 | MSCI,MSCI,Financials
136 | CSX,CSX Corp.,Industrials
137 | CMI,Cummins Inc.,Industrials
138 | CVS,CVS Health,Consumer Staples
139 | DHI,D. R. Horton,Consumer Discretionary
140 | DHR,Danaher Corp.,Health Care
141 | DRI,Darden Restaurants,Consumer Discretionary
142 | DVA,DaVita Inc.,Health Care
143 | DE,Deere & Co.,Industrials
144 | DAL,Delta Air Lines Inc.,Industrials
145 | XRAY,Dentsply Sirona,Health Care
146 | DVN,Devon Energy Corp.,Energy
147 | DLR,Digital Realty Trust Inc,Real Estate
148 | DFS,Discover Financial Services,Financials
149 | DISCA,Discovery Inc. Class A,Consumer Discretionary
150 | DISCK,Discovery Inc. Class C,Consumer Discretionary
151 | DISH,Dish Network,Consumer Discretionary
152 | DG,Dollar General,Consumer Discretionary
153 | DLTR,Dollar Tree,Consumer Discretionary
154 | D,Dominion Energy,Utilities
155 | DOV,Dover Corp.,Industrials
156 | DWDP,DowDuPont,Materials
157 | DPS,Dr Pepper Snapple Group,Consumer Staples
158 | DTE,DTE Energy Co.,Utilities
159 | DUK,Duke Energy,Utilities
160 | DRE,Duke Realty Corp,Real Estate
161 | DXC,DXC Technology,Information Technology
162 | ETFC,E*Trade,Financials
163 | EMN,Eastman Chemical,Materials
164 | ETN,Eaton Corporation,Industrials
165 | EBAY,eBay Inc.,Information Technology
166 | ECL,Ecolab Inc.,Materials
167 | EIX,Edison Int'l,Utilities
168 | EW,Edwards Lifesciences,Health Care
169 | EA,Electronic Arts,Information Technology
170 | EMR,Emerson Electric Company,Industrials
171 | ETR,Entergy Corp.,Utilities
172 | EVHC,Envision Healthcare,Health Care
173 | EOG,EOG Resources,Energy
174 | EQT,EQT Corporation,Energy
175 | EFX,Equifax Inc.,Industrials
176 | EQIX,Equinix,Real Estate
177 | EQR,Equity Residential,Real Estate
178 | ESS,"Essex Property Trust, Inc.",Real Estate
179 | EL,Estee Lauder Cos.,Consumer Staples
180 | RE,Everest Re Group Ltd.,Financials
181 | ES,Eversource Energy,Utilities
182 | EXC,Exelon Corp.,Utilities
183 | EXPE,Expedia Inc.,Consumer Discretionary
184 | EXPD,Expeditors International,Industrials
185 | ESRX,Express Scripts,Health Care
186 | EXR,Extra Space Storage,Real Estate
187 | XOM,Exxon Mobil Corp.,Energy
188 | FFIV,F5 Networks,Information Technology
189 | FB,"Facebook, Inc.",Information Technology
190 | FAST,Fastenal Co,Industrials
191 | FRT,Federal Realty Investment Trust,Real Estate
192 | FDX,FedEx Corporation,Industrials
193 | FIS,Fidelity National Information Services,Information Technology
194 | FITB,Fifth Third Bancorp,Financials
195 | FE,FirstEnergy Corp,Utilities
196 | FISV,Fiserv Inc,Information Technology
197 | FLIR,FLIR Systems,Information Technology
198 | FLS,Flowserve Corporation,Industrials
199 | FLR,Fluor Corp.,Industrials
200 | FMC,FMC Corporation,Materials
201 | FL,Foot Locker Inc,Consumer Discretionary
202 | F,Ford Motor,Consumer Discretionary
203 | FTV,Fortive Corp,Industrials
204 | FBHS,Fortune Brands Home & Security,Industrials
205 | BEN,Franklin Resources,Financials
206 | FCX,Freeport-McMoRan Inc.,Materials
207 | GPS,Gap Inc.,Consumer Discretionary
208 | GRMN,Garmin Ltd.,Consumer Discretionary
209 | IT,Gartner Inc,Information Technology
210 | GD,General Dynamics,Industrials
211 | GE,General Electric,Industrials
212 | GGP,General Growth Properties Inc.,Real Estate
213 | GIS,General Mills,Consumer Staples
214 | GM,General Motors,Consumer Discretionary
215 | GPC,Genuine Parts,Consumer Discretionary
216 | GILD,Gilead Sciences,Health Care
217 | GPN,Global Payments Inc.,Information Technology
218 | GS,Goldman Sachs Group,Financials
219 | GT,Goodyear Tire & Rubber,Consumer Discretionary
220 | GWW,Grainger (W.W.) Inc.,Industrials
221 | HAL,Halliburton Co.,Energy
222 | HBI,Hanesbrands Inc,Consumer Discretionary
223 | HOG,Harley-Davidson,Consumer Discretionary
224 | HRS,Harris Corporation,Information Technology
225 | HIG,Hartford Financial Svc.Gp.,Financials
226 | HAS,Hasbro Inc.,Consumer Discretionary
227 | HCA,HCA Holdings,Health Care
228 | HCP,HCP Inc.,Real Estate
229 | HP,Helmerich & Payne,Energy
230 | HSIC,Henry Schein,Health Care
231 | HES,Hess Corporation,Energy
232 | HPE,Hewlett Packard Enterprise,Information Technology
233 | HLT,Hilton Worldwide Holdings Inc,Consumer Discretionary
234 | HOLX,Hologic,Health Care
235 | HD,Home Depot,Consumer Discretionary
236 | HON,Honeywell Int'l Inc.,Industrials
237 | HRL,Hormel Foods Corp.,Consumer Staples
238 | HST,Host Hotels & Resorts,Real Estate
239 | HPQ,HP Inc.,Information Technology
240 | HUM,Humana Inc.,Health Care
241 | HBAN,Huntington Bancshares,Financials
242 | HII,Huntington Ingalls Industries,Industrials
243 | IDXX,IDEXX Laboratories,Health Care
244 | INFO,IHS Markit Ltd.,Industrials
245 | ITW,Illinois Tool Works,Industrials
246 | ILMN,Illumina Inc,Health Care
247 | INCY,Incyte,Health Care
248 | IR,Ingersoll-Rand PLC,Industrials
249 | INTC,Intel Corp.,Information Technology
250 | ICE,Intercontinental Exchange,Financials
251 | IBM,International Business Machines,Information Technology
252 | IP,International Paper,Materials
253 | IPG,Interpublic Group,Consumer Discretionary
254 | IFF,Intl Flavors & Fragrances,Materials
255 | INTU,Intuit Inc.,Information Technology
256 | ISRG,Intuitive Surgical Inc.,Health Care
257 | IVZ,Invesco Ltd.,Financials
258 | IPGP,IPG Photonics Corp.,Information Technology
259 | IQV,IQVIA Holdings Inc.,Health Care
260 | IRM,Iron Mountain Incorporated,Real Estate
261 | JBHT,J. B. Hunt Transport Services,Industrials
262 | JEC,Jacobs Engineering Group,Industrials
263 | SJM,JM Smucker,Consumer Staples
264 | JNJ,Johnson & Johnson,Health Care
265 | JCI,Johnson Controls International,Industrials
266 | JPM,JPMorgan Chase & Co.,Financials
267 | JNPR,Juniper Networks,Information Technology
268 | KSU,Kansas City Southern,Industrials
269 | K,Kellogg Co.,Consumer Staples
270 | KEY,KeyCorp,Financials
271 | KMB,Kimberly-Clark,Consumer Staples
272 | KIM,Kimco Realty,Real Estate
273 | KMI,Kinder Morgan,Energy
274 | KLAC,KLA-Tencor Corp.,Information Technology
275 | KSS,Kohl's Corp.,Consumer Discretionary
276 | KHC,Kraft Heinz Co,Consumer Staples
277 | KR,Kroger Co.,Consumer Staples
278 | LB,L Brands Inc.,Consumer Discretionary
279 | LLL,L-3 Communications Holdings,Industrials
280 | LH,Laboratory Corp. of America Holding,Health Care
281 | LRCX,Lam Research,Information Technology
282 | LEG,Leggett & Platt,Consumer Discretionary
283 | LEN,Lennar Corp.,Consumer Discretionary
284 | JEF,Jefferies Financial Group Inc.,Financials
285 | LLY,Lilly (Eli) & Co.,Health Care
286 | LNC,Lincoln National,Financials
287 | LKQ,LKQ Corporation,Consumer Discretionary
288 | LMT,Lockheed Martin Corp.,Industrials
289 | L,Loews Corp.,Financials
290 | LOW,Lowe's Cos.,Consumer Discretionary
291 | LYB,LyondellBasell,Materials
292 | MTB,M&T Bank Corp.,Financials
293 | MAC,Macerich,Real Estate
294 | M,Macy's Inc.,Consumer Discretionary
295 | MRO,Marathon Oil Corp.,Energy
296 | MPC,Marathon Petroleum,Energy
297 | MAR,Marriott Int'l.,Consumer Discretionary
298 | MMC,Marsh & McLennan,Financials
299 | MLM,Martin Marietta Materials,Materials
300 | MAS,Masco Corp.,Industrials
301 | MA,Mastercard Inc.,Information Technology
302 | MAT,Mattel Inc.,Consumer Discretionary
303 | MKC,McCormick & Co.,Consumer Staples
304 | MCD,McDonald's Corp.,Consumer Discretionary
305 | MCK,McKesson Corp.,Health Care
306 | MDT,Medtronic plc,Health Care
307 | MRK,Merck & Co.,Health Care
308 | MET,MetLife Inc.,Financials
309 | MTD,Mettler Toledo,Health Care
310 | MGM,MGM Resorts International,Consumer Discretionary
311 | KORS,Michael Kors Holdings,Consumer Discretionary
312 | MCHP,Microchip Technology,Information Technology
313 | MU,Micron Technology,Information Technology
314 | MSFT,Microsoft Corp.,Information Technology
315 | MAA,Mid-America Apartments,Real Estate
316 | MHK,Mohawk Industries,Consumer Discretionary
317 | TAP,Molson Coors Brewing Company,Consumer Staples
318 | MDLZ,Mondelez International,Consumer Staples
319 | MON,Monsanto Co.,Materials
320 | MNST,Monster Beverage,Consumer Staples
321 | MCO,Moody's Corp,Financials
322 | MS,Morgan Stanley,Financials
323 | MSI,Motorola Solutions Inc.,Information Technology
324 | MYL,Mylan N.V.,Health Care
325 | NDAQ,"Nasdaq, Inc.",Financials
326 | NOV,National Oilwell Varco Inc.,Energy
327 | NAVI,Navient,Financials
328 | NKTR,Nektar Therapeutics,Health Care
329 | NTAP,NetApp,Information Technology
330 | NFLX,Netflix Inc.,Information Technology
331 | NWL,Newell Brands,Consumer Discretionary
332 | NFX,Newfield Exploration Co,Energy
333 | NEM,Newmont Mining Corporation,Materials
334 | NWSA,News Corp. Class A,Consumer Discretionary
335 | NWS,News Corp. Class B,Consumer Discretionary
336 | NEE,NextEra Energy,Utilities
337 | NLSN,Nielsen Holdings,Industrials
338 | NKE,Nike,Consumer Discretionary
339 | NI,NiSource Inc.,Utilities
340 | NBL,Noble Energy Inc,Energy
341 | JWN,Nordstrom,Consumer Discretionary
342 | NSC,Norfolk Southern Corp.,Industrials
343 | NTRS,Northern Trust Corp.,Financials
344 | NOC,Northrop Grumman Corp.,Industrials
345 | NCLH,Norwegian Cruise Line,Consumer Discretionary
346 | NRG,NRG Energy,Utilities
347 | NUE,Nucor Corp.,Materials
348 | NVDA,Nvidia Corporation,Information Technology
349 | ORLY,O'Reilly Automotive,Consumer Discretionary
350 | OXY,Occidental Petroleum,Energy
351 | OMC,Omnicom Group,Consumer Discretionary
352 | OKE,ONEOK,Energy
353 | ORCL,Oracle Corp.,Information Technology
354 | PCAR,PACCAR Inc.,Industrials
355 | PKG,Packaging Corporation of America,Materials
356 | PH,Parker-Hannifin,Industrials
357 | PAYX,Paychex Inc.,Information Technology
358 | PYPL,PayPal,Information Technology
359 | PNR,Pentair Ltd.,Industrials
360 | PBCT,People's United Financial,Financials
361 | PEP,PepsiCo Inc.,Consumer Staples
362 | PKI,PerkinElmer,Health Care
363 | PRGO,Perrigo,Health Care
364 | PFE,Pfizer Inc.,Health Care
365 | PCG,PG&E Corp.,Utilities
366 | PM,Philip Morris International,Consumer Staples
367 | PSX,Phillips 66,Energy
368 | PNW,Pinnacle West Capital,Utilities
369 | PXD,Pioneer Natural Resources,Energy
370 | PNC,PNC Financial Services,Financials
371 | RL,Polo Ralph Lauren Corp.,Consumer Discretionary
372 | PPG,PPG Industries,Materials
373 | PPL,PPL Corp.,Utilities
374 | PX,Praxair Inc.,Materials
375 | PFG,Principal Financial Group,Financials
376 | PG,Procter & Gamble,Consumer Staples
377 | PGR,Progressive Corp.,Financials
378 | PLD,Prologis,Real Estate
379 | PRU,Prudential Financial,Financials
380 | PEG,Public Serv. Enterprise Inc.,Utilities
381 | PSA,Public Storage,Real Estate
382 | PHM,Pulte Homes Inc.,Consumer Discretionary
383 | PVH,PVH Corp.,Consumer Discretionary
384 | QRVO,Qorvo,Information Technology
385 | QCOM,QUALCOMM Inc.,Information Technology
386 | PWR,Quanta Services Inc.,Industrials
387 | DGX,Quest Diagnostics,Health Care
388 | RRC,Range Resources Corp.,Energy
389 | RJF,Raymond James Financial Inc.,Financials
390 | RTN,Raytheon Co.,Industrials
391 | O,Realty Income Corporation,Real Estate
392 | RHT,Red Hat Inc.,Information Technology
393 | REG,Regency Centers Corporation,Real Estate
394 | REGN,Regeneron,Health Care
395 | RF,Regions Financial Corp.,Financials
396 | RSG,Republic Services Inc,Industrials
397 | RMD,ResMed,Health Care
398 | RHI,Robert Half International,Industrials
399 | ROK,Rockwell Automation Inc.,Industrials
400 | COL,Rockwell Collins,Industrials
401 | ROP,Roper Technologies,Industrials
402 | ROST,Ross Stores,Consumer Discretionary
403 | RCL,Royal Caribbean Cruises Ltd,Consumer Discretionary
404 | SPGI,"S&P Global, Inc.",Financials
405 | CRM,Salesforce.com,Information Technology
406 | SBAC,SBA Communications,Real Estate
407 | SCG,SCANA Corp,Utilities
408 | SLB,Schlumberger Ltd.,Energy
409 | STX,Seagate Technology,Information Technology
410 | SEE,Sealed Air,Materials
411 | SRE,Sempra Energy,Utilities
412 | SHW,Sherwin-Williams,Materials
413 | SPG,Simon Property Group Inc,Real Estate
414 | SWKS,Skyworks Solutions,Information Technology
415 | SLG,SL Green Realty,Real Estate
416 | SNA,Snap-On Inc.,Consumer Discretionary
417 | SO,Southern Co.,Utilities
418 | LUV,Southwest Airlines,Industrials
419 | SWK,Stanley Black & Decker,Consumer Discretionary
420 | SBUX,Starbucks Corp.,Consumer Discretionary
421 | STT,State Street Corp.,Financials
422 | SRCL,Stericycle Inc,Industrials
423 | SYK,Stryker Corp.,Health Care
424 | STI,SunTrust Banks,Financials
425 | SIVB,SVB Financial,Financials
426 | SYMC,Symantec Corp.,Information Technology
427 | SYF,Synchrony Financial,Financials
428 | SNPS,Synopsys Inc.,Information Technology
429 | SYY,Sysco Corp.,Consumer Staples
430 | TROW,T. Rowe Price Group,Financials
431 | TTWO,Take-Two Interactive,Information Technology
432 | TPR,"Tapestry, Inc.",Consumer Discretionary
433 | TGT,Target Corp.,Consumer Discretionary
434 | TEL,TE Connectivity Ltd.,Information Technology
435 | FTI,TechnipFMC,Energy
436 | TXN,Texas Instruments,Information Technology
437 | TXT,Textron Inc.,Industrials
438 | BK,The Bank of New York Mellon Corp.,Financials
439 | CLX,The Clorox Company,Consumer Staples
440 | COO,The Cooper Companies,Health Care
441 | HSY,The Hershey Company,Consumer Staples
442 | MOS,The Mosaic Company,Materials
443 | TRV,The Travelers Companies Inc.,Financials
444 | DIS,The Walt Disney Company,Consumer Discretionary
445 | TMO,Thermo Fisher Scientific,Health Care
446 | TIF,Tiffany & Co.,Consumer Discretionary
447 | TWX,Time Warner Inc.,Consumer Discretionary
448 | TJX,TJX Companies Inc.,Consumer Discretionary
449 | TMK,Torchmark Corp.,Financials
450 | TSS,Total System Services,Information Technology
451 | TSCO,Tractor Supply Company,Consumer Discretionary
452 | TDG,TransDigm Group,Industrials
453 | TRIP,TripAdvisor,Consumer Discretionary
454 | FOXA,Twenty-First Century Fox Class A,Consumer Discretionary
455 | FOX,Twenty-First Century Fox Class B,Consumer Discretionary
456 | TSN,Tyson Foods,Consumer Staples
457 | USB,U.S. Bancorp,Financials
458 | UDR,UDR Inc,Real Estate
459 | ULTA,Ulta Beauty,Consumer Discretionary
460 | UAA,Under Armour Class A,Consumer Discretionary
461 | UA,Under Armour Class C,Consumer Discretionary
462 | UNP,Union Pacific,Industrials
463 | UAL,United Continental Holdings,Industrials
464 | UNH,United Health Group Inc.,Health Care
465 | UPS,United Parcel Service,Industrials
466 | URI,"United Rentals, Inc.",Industrials
467 | UTX,United Technologies,Industrials
468 | UHS,"Universal Health Services, Inc.",Health Care
469 | UNM,Unum Group,Financials
470 | VFC,V.F. Corp.,Consumer Discretionary
471 | VLO,Valero Energy,Energy
472 | VAR,Varian Medical Systems,Health Care
473 | VTR,Ventas Inc,Real Estate
474 | VRSN,Verisign Inc.,Information Technology
475 | VRSK,Verisk Analytics,Industrials
476 | VZ,Verizon Communications,Telecommunication Services
477 | VRTX,Vertex Pharmaceuticals Inc,Health Care
478 | VIAB,Viacom Inc.,Consumer Discretionary
479 | V,Visa Inc.,Information Technology
480 | VNO,Vornado Realty Trust,Real Estate
481 | VMC,Vulcan Materials,Materials
482 | WMT,Wal-Mart Stores,Consumer Staples
483 | WBA,Walgreens Boots Alliance,Consumer Staples
484 | WM,Waste Management Inc.,Industrials
485 | WAT,Waters Corporation,Health Care
486 | WEC,Wec Energy Group Inc,Utilities
487 | WFC,Wells Fargo,Financials
488 | WELL,Welltower Inc.,Real Estate
489 | WDC,Western Digital,Information Technology
490 | WU,Western Union Co,Information Technology
491 | WRK,WestRock Company,Materials
492 | WY,Weyerhaeuser Corp.,Real Estate
493 | WHR,Whirlpool Corp.,Consumer Discretionary
494 | WMB,Williams Cos.,Energy
495 | WLTW,Willis Towers Watson,Financials
496 | ABMD,Abiomed Inc.,Health Care
497 | WYNN,Wynn Resorts Ltd,Consumer Discretionary
498 | XEL,Xcel Energy Inc,Utilities
499 | XRX,Xerox Corp.,Information Technology
500 | XLNX,Xilinx Inc,Information Technology
501 | XL,XL Capital,Financials
502 | XYL,Xylem Inc.,Industrials
503 | YUM,Yum! Brands Inc,Consumer Discretionary
504 | ZBH,Zimmer Biomet Holdings,Health Care
505 | ZION,Zions Bancorp,Financials
506 | ZTS,Zoetis,Health Care
507 |
--------------------------------------------------------------------------------