Jack of all Trades

Program is capable of using information gathered from a web service to obtain past prices of stocks, calculate some metrics summarizing those prices, and ultimately report on potentially opportune times to buy or sell the stock, based on one of a few automated buying-and-selling strategies.

'''MODULE 1:''' import IndicatorsClasses import readingURL import urllib.request import json def analyzeHeader(symbol: str) -> None: '''accesses the readingURL module to connect a company to the given symbol. Passes this information in a list to get it correctly formatted.''' access = readingURL.read() company, shares = access.readURL(symbol) info = [str(symbol), str(company), str(shares)] print(symbol) print(company) print(shares) table_Header = [['Date', 'Open', 'High', 'Low', 'Close', 'Volume' , 'Indicator', 'Buy?', 'Sell?']] tabFormat(table_Header) def tabFormat(info: list) -> None: '''takes a list and correctly applies tab formatting.''' for eachList in range(len(info)): tempStr = "" for component in info[eachList][:-1]: tempStr += str(component) + "\t" tempStr += str(info[eachList][-1]) print(tempStr) if __name__ == "__main__": symbol = input().upper() while True: try: n = int(input()) if n < 0 or n > 999: print("Invalid trading days. Please try again.") n = int(input()) else: break except ValueError: print("Invalid input. Please submit an integer.") combination = input() if "TR" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateRange(combination) listDetails = callClass.calculate(symbol, n) tabFormat(listDetails) elif "MP" in combination or "MV" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateAverage(combination) if "MP" in combination: listDetails = callClass.calculate(symbol, n) else: listDetails = callClass.calculateVolume(symbol, n) tabFormat(listDetails) elif "DP" in combination or "DV" in combination: analyzeHeader(symbol) callClass = IndicatorsClasses.calculateDirectional(combination) if "DP" in combination: listDetails = callClass.calculate(symbol, n) else: listDetails = callClass.calculateVolume(symbol, n) tabFormat(listDetails) else: print("Input Error (Line 3): Must specify a legal indicator and signal strategy on this line") print("Data provided for free by IEX") print("View IEX's Terms of Use") print("https://iextrading.com/api-exhibit-a/") '''MODULE 2:''' import urllib.request import json class read: def readURL(self, symbol: str) -> (str, int): '''looks through the list of available companies participating in the stock exchange by matching their symbol to the uses''' response = urllib.request.urlopen("https://api.iextrading.com/1.0/stock/market/previous") data = response.read() response.close() text = json.loads(data.decode(encoding = 'utf-8')) symbolsList = [] for entity in text: symbolsList.append(entity) if symbol in symbolsList: company = self.readSymbols(symbol) outShares = self.analyzeShares(symbol) return company, outShares else: print("Not a valid symbol.") def readSymbols(self, symbol: str) -> str: '''matches the symbol to a company name''' postOpening = urllib.request.urlopen("https://api.iextrading.com/1.0/ref-data/symbols") valueData = postOpening.read() postOpening.close() matchList = json.loads(valueData.decode(encoding = 'utf-8')) for eachDict in matchList: for key, value in eachDict.items(): if symbol == value: name = eachDict["name"] return name def analyzeShares(self, symbol: str) -> int: '''replaces the URL's query with the search symbol and analyzes the data from this URL''' newURL = "https://api.iextrading.com/1.0/stock/" + symbol + "/stats" openThis = urllib.request.urlopen(newURL) readInfo = openThis.read() openThis.close() compValues = json.loads(readInfo.decode(encoding = 'utf-8')) for component in compValues: outstanding = compValues["sharesOutstanding"] return outstanding def additionalInfo(self, symbol: str, backNDays: int) -> [[]]: '''finds information for the "date, opening price, highest price, lowest price, closing price, and volume''' newURL = "https://api.iextrading.com/1.0/stock/" + symbol + "/chart/6m" response = urllib.request.urlopen(newURL) read = response.read() response.close() data = json.loads(read.decode(encoding = 'utf-8')) infoNestList = [] for component in data[-backNDays:]: date = component["date"] openP = ('%.4f' %(component["open"])) high = ('%.4f' %(component["high"])) low = ('%.4f' %(component["low"])) closeP = ('%.4f' %(component["close"])) volume = ('%d' %(component["volume"])) infoNestList.append([date, openP, high, low, closeP, volume]) return infoNestList '''MODULE 3:''' class true_range: '''after called on from calcRange in "IndicatorsClass" module, iterates through nested list containing TR information, and determines if should buy (TR/ previous closing value> buyThreshold) or should sell (TR/ previous closing value < sellThreshold), or neither.''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: strategy = [] nestDetails[0].append("") nestDetails[0].append("") buyList = [] sellList = [] for eachDay in range(1, len(nestDetails)): prevClosePrice = nestDetails[eachDay-1][4] trueRange = nestDetails[eachDay][-1] if ">" in buyThreshold: if float(trueRange) <= float(buyThreshold[1:]): nestDetails[eachDay].append("") else: nestDetails[eachDay].append("BUY") else: if float(trueRange) < float(buyThreshold[1:]): nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append("") if ">" in sellThreshold: if float(trueRange) >= float(sellThreshold[1:]): nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") else: if float(trueRange) < float(sellThreshold[1:]): nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") return nestDetails class simple_moving: '''after called on from calcAvg in "IndicatorsClass" module, iterates through nested list containing SMA information, and determines if should buy (if prev day's closing price < its SMA AND opposite for today) or should sell (if prev day's closing price > its SMA AND opposite for today), or neither.''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: nestDetails[0].append("") nestDetails[0].append("") for eachDay in range(1,len(nestDetails)): SMA = nestDetails[eachDay][-1] closingPrice = nestDetails[eachDay][4] prevDaySMA = nestDetails[eachDay-1][6] prevDayClosing = nestDetails[eachDay-1][4] if closingPrice > SMA: if prevDayClosing < prevDaySMA: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') if closingPrice < SMA: if prevDayClosing > prevDaySMA: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') return nestDetails def simple_moving_volume(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: '''after called on from calcAvg in "IndicatorsClass" module, iterates through nested list containing SMA information, and determines if should buy (if prev day's closing price < its SMA AND opposite for today) or should sell (if prev day's closing price > its SMA AND opposite for today), or neither.''' nestDetails[0].append("") nestDetails[0].append("") for eachDay in range(1,len(nestDetails)): SMA = nestDetails[eachDay][-1] volume = nestDetails[eachDay][5] prevDaySMA = nestDetails[eachDay-1][-1] prevDayVolume = nestDetails[eachDay-1][5] if volume > SMA: if prevDayVolume < prevDaySMA: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') if volume < SMA: if prevDayVolume > prevDaySMA: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append('') else: nestDetails[eachDay].append('') return nestDetails class directional: '''after called on from calcDirec in "IndicatorsClass" module, iterates through nested list containing indicator information, and determines if should buy (daily indicator > buy threshold) or should sell (daily indicator < sell threshold)''' def signal(self, nestDetails: [[]], buyThreshold: str, sellThreshold: str, calcN: int) -> [[]]: buyThreshold = float(buyThreshold) sellThreshold = float(sellThreshold) for eachDay in range(len(nestDetails)): todayIndicator = nestDetails[eachDay][6] prevIndicator = nestDetails[eachDay-1][6] if "+" in todayIndicator: todayIndicator = todayIndicator[1:] todayIndicator = float(todayIndicator) if "+" in prevIndicator: prevIndicator = prevIndicator[1:] prevIndicator = float(prevIndicator) if todayIndicator > buyThreshold: if prevIndicator <= buyThreshold: nestDetails[eachDay].append("BUY") else: nestDetails[eachDay].append("") else: nestDetails[eachDay].append("") if todayIndicator < sellThreshold: if prevIndicator >= sellThreshold: nestDetails[eachDay].append("SELL") else: nestDetails[eachDay].append("") else: nestDetails[eachDay].append("") return nestDetails '''MODULE 4:''' import readingURL import StrategiesClasses class calculateRange: def __init__(self, combination) -> [[]]: '''initializes the basic variables used throughout the calculate function.''' split = combination.split() self._indicator = split[0] self._buyThreshold = split[1] self._sellThreshold = split[2] def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, N) TRList = [""] for eachDay in range(1,N): highest = float(nestDetails[eachDay][2]) lowest = float(nestDetails[eachDay][3]) prevClosing = float(nestDetails[eachDay-1][4]) if prevClosing > highest: highest = prevClosing if prevClosing < lowest: lowest = prevClosing TRange = "%.4f"%(100*((highest - lowest)/prevClosing)) TRList.append(TRange) for each in range(len(nestDetails)): nestDetails[each].append(TRList[each]) calcN = 0 buyOrSell = StrategiesClasses.true_range() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails class calculateAverage: def __init__(self, combination) -> [[]]: '''initializes the basic variables used throughout the calculate function.''' split = combination.split() self._indicator = split[0] self._calcN = int(split[1]) def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' printN = N calcN = self._calcN accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) SMAList = [] closePriceList = [] for eachDay in range(len(nestDetails)): closePrice = float(nestDetails[eachDay][4]) closePriceList.append(closePrice) if eachDay < calcN-1: nestDetails[eachDay].append("") if eachDay == calcN-1: SMA = '%.4f'%(sum(closePriceList)/calcN) SMAList.append(SMA) elif eachDay > calcN-1: tot = (sum(closePriceList[eachDay-calcN:eachDay])) SMA = "%.4f"%(float(tot)/float(calcN)) SMAList.append(SMA) nestDetails[eachDay-1].append(SMA) if printN >= calcN: lastSMA = "%.4f" %(sum(closePriceList[-calcN:])/calcN) nestDetails[printN-1].append(lastSMA) self._buyThreshold = 0 self._sellThreshold = 0 buyOrSell = StrategiesClasses.simple_moving() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails def calculateVolume(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing volumes) and appends to the end of each list in a nested list.''' printN = N calcN = self._calcN accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) SMAList = [] volumeList = [] for eachDay in range(len(nestDetails)): volume = float(nestDetails[eachDay][5]) volumeList.append(volume) if eachDay < calcN-1: nestDetails[eachDay].append("") if eachDay == calcN-1: SMA = '%.4f'%(sum(volumeList)/calcN) SMAList.append(SMA) elif eachDay > calcN-1: total = (sum(volumeList[eachDay-calcN:eachDay])) SMA = "%.4f"%(float(total)/float(calcN)) SMAList.append(SMA) nestDetails[eachDay-1].append(SMA) if printN >= calcN: lastSMA = "%.4f" %(sum(volumeList[-calcN:])/calcN) nestDetails[printN-1].append(lastSMA) self._buyThreshold = 0 self._sellThreshold = 0 buyOrSell = StrategiesClasses.simple_moving() nestDetails = buyOrSell.simple_moving_volume(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails class calculateDirectional: def __init__(self, combination): split = combination.split() self._indicator = split[0] self._calcN = int(split[1]) self._buyThreshold = split[2] self._sellThreshold = split[3] def calculate(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing closing prices) and appends to the end of each list in a nested list.''' calcN = self._calcN printN = N accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) permList = [] closing = [] for eachDay in range(printN): tempList = [] closing.append(nestDetails[eachDay][4]) for n in range(eachDay-calcN, eachDay+1): if n >= 0: tempList.append(closing[n]) indicator = 0 for loopDay in range(len(tempList)): if loopDay-1 >=0: if float(tempList[loopDay]) > float(tempList[loopDay-1]): indicator+=1 elif float(tempList[loopDay]) < float(tempList[loopDay-1]): indicator-=1 if indicator > 0: sign = "+"+str(indicator) permList.append(sign) elif indicator < 0: permList.append(str(indicator)) else: permList.append("0") for eachDay in range(len(permList)): nestDetails[eachDay].append(permList[eachDay]) calcN = 0 buyOrSell = StrategiesClasses.directional() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails def calculateVolume(self, symbol, N) -> [[]]: '''calculates the indicator value (based on comparing volumes) and appends to the end of each list in a nested list.''' calcN = self._calcN printN = N accessAPI = readingURL.read() nestDetails = accessAPI.additionalInfo(symbol, printN) permList = [] volume = [] for eachDay in range(printN): tempList = [] volume.append(nestDetails[eachDay][5]) for n in range(eachDay-calcN, eachDay+1): if n >= 0: tempList.append(volume[n]) indicator = 0 for loopDay in range(len(tempList)): if loopDay-1 >=0: if float(tempList[loopDay]) > float(tempList[loopDay-1]): indicator+=1 elif float(tempList[loopDay]) < float(tempList[loopDay-1]): indicator-=1 if indicator > 0: sign = "+"+str(indicator) permList.append(sign) elif indicator < 0: permList.append(str(indicator)) else: permList.append("0") for eachDay in range(len(permList)): nestDetails[eachDay].append(permList[eachDay]) calcN = 0 buyOrSell = StrategiesClasses.directional() nestDetails = buyOrSell.signal(nestDetails, self._buyThreshold, self._sellThreshold, calcN) return nestDetails
1