
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