├── .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 | --------------------------------------------------------------------------------