├── .DS_Store
├── .gitattributes
├── .vscode
└── settings.json
├── README.md
├── autoGit.sh
├── autoPIP.sh
├── calls heat map.png
├── out.png
├── puts heat map.png
└── src
├── GUI.py
├── __pycache__
├── GUI.cpython-37.pyc
├── GUI.cpython-38.pyc
└── scrolly.cpython-37.pyc
├── main.py
└── scrolly.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/.DS_Store
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.pythonPath": "/usr/bin/python3"
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OptionsAnalyzer (BETA VERSION)
2 |
3 | **************************************
4 |
5 |
6 | If you encounter a ZSH segmentation fault OR want to try the updated version
7 | click here
8 |
9 |
10 | **************************************
11 |
12 | * Run autoPIP.sh by typing sh autoPIP.sh in terminal in your IDE to install all required modules
13 |
14 | ## How to use the text menu
15 | *Commands are: setticker, setstrike, optionchain, OIimage, CallsVolumeMap, CallsOIMap, PutsVolumeMap, PutsOIMap, CallVolume3D, CallOI3D, PutVolume3D, PutOI3D, exit*
16 | * Start with *setticker* and enter a desired stock (ex: AMD)
17 | * Next, enter *setstrike* to pick a desired date (enables further methods like OIimage and optionchain)
18 | * After these steps have been completed in order, you're free to load a heatmap or 3D bar graph of the option's open interest or volume for every date and strike, as well as a PNG of open interest / volume on a certain date
19 |
20 | ### Open Interest 3D Graph
21 | 
22 |
23 | ### Open Interest Heatmap
24 | 
25 |
26 |
27 | ### Volume 3D Graph
28 | 
29 |
30 |
31 | ### Interest at each strike on a date
32 | 
33 |
34 | ### Text representation of Open Interest for each date and strike
35 |
36 |
37 |
38 | ### Text option chain
39 |
40 |
--------------------------------------------------------------------------------
/autoGit.sh:
--------------------------------------------------------------------------------
1 | git add .
2 | git commit -m "AutoGIT Commit"
3 | git push
4 |
--------------------------------------------------------------------------------
/autoPIP.sh:
--------------------------------------------------------------------------------
1 | pip3 install seaborn
2 | pip3 install matplotlib
3 | pip3 install yfinance
4 | pip3 install numpy
5 | pip3 install pandas
6 | pip3 install Image
7 | pip3 install wx
8 | pip3 install PyQt5
9 |
--------------------------------------------------------------------------------
/calls heat map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/calls heat map.png
--------------------------------------------------------------------------------
/out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/out.png
--------------------------------------------------------------------------------
/puts heat map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/puts heat map.png
--------------------------------------------------------------------------------
/src/GUI.py:
--------------------------------------------------------------------------------
1 | import wx
2 |
3 | choice = "test"
4 |
5 |
6 | def onButton():
7 | print("Button pressed.")
8 |
9 | app = wx.App()
10 | frame = wx.Frame(None, -1, 'win.py')
11 | frame.SetDimensions(0, 0, 200, 50)
12 | dlg = wx.TextEntryDialog(frame, 'Enter a Stock Ticker', 'Text Entry')
13 | dlg.SetValue("AMD")
14 | if dlg.ShowModal() == wx.ID_OK:
15 | # print('Ticker entered was: %s\n' % dlg.GetValue())
16 | return dlg.GetValue()
17 | dlg.Destroy()
18 |
19 |
20 | def pickStrikePrice(choices):
21 |
22 | class MyFrame(wx.Frame):
23 | def __init__(self, parent, title):
24 | super(MyFrame, self).__init__(parent, title=title, size=(400, 200))
25 | self.panel = MyPanel(self)
26 |
27 | class MyPanel(wx.Panel):
28 | def __init__(self, parent):
29 | super(MyPanel, self).__init__(parent)
30 | self.label = wx.StaticText(
31 | self, label="Pick a Strike Date:", pos=(50, 30))
32 | languages = choices
33 | self.combobox = wx.ComboBox(self, choices=languages, pos=(50, 50))
34 | self.label2 = wx.StaticText(self, label="", pos=(50, 80))
35 | self.Bind(wx.EVT_COMBOBOX, self.OnCombo)
36 |
37 | def OnCombo(self, event):
38 | self.label2.SetLabel("You picked " + self.combobox.GetValue())
39 | global choice
40 | choice = self.combobox.GetValue()
41 | app.ExitMainLoop()
42 |
43 | class MyApp(wx.App):
44 | def OnInit(self):
45 | self.frame = MyFrame(parent=None, title="Strike Picker")
46 | self.frame.Show()
47 | return True
48 |
49 | app = MyApp()
50 | app.MainLoop()
51 |
52 |
53 | def returnChoice():
54 | global choice
55 | return choice
56 |
--------------------------------------------------------------------------------
/src/__pycache__/GUI.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/src/__pycache__/GUI.cpython-37.pyc
--------------------------------------------------------------------------------
/src/__pycache__/GUI.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/src/__pycache__/GUI.cpython-38.pyc
--------------------------------------------------------------------------------
/src/__pycache__/scrolly.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamPom100/OptionsAnalyzer/4269476a1a86657ecb95cf7d8a56330a30504107/src/__pycache__/scrolly.cpython-37.pyc
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | from mpl_toolkits.mplot3d import Axes3D
2 | import yfinance as yf
3 | import pandas as pd
4 | import matplotlib.pyplot as plt
5 | from matplotlib import cm
6 | from PIL import Image
7 | from GUI import *
8 | import seaborn as sb
9 | import numpy as np
10 | import sys
11 |
12 |
13 | # pre-set values
14 |
15 | ticker = "AMD"
16 | DateArray = yf.Ticker(ticker).options
17 | strikeChoice = DateArray[2]
18 | opt = yf.Ticker(ticker).option_chain(strikeChoice)
19 | calls = opt.calls
20 | puts = opt.puts
21 | ArrayStore = None
22 |
23 |
24 | def askForTicker():
25 | # Prompt the user for a new ticker
26 | global ticker, strikeChoice, opt
27 | ticker = onButton()
28 | print("Ticker was: " + ticker)
29 | # pickAStrike2() # <----- causes ZSH error (toDO)
30 |
31 |
32 | def getOptionsChain(inputString):
33 | # Retrieve the Options Chain Expiration dates from Yahoo Finance
34 | YFticker = yf.Ticker(inputString)
35 | global DateArray
36 | tempTuple = ("Pick a Strike Price",)
37 | DateArray = tempTuple+YFticker.options
38 |
39 |
40 | def pickAStrike():
41 | global strikeChoice, opt
42 | pickStrikePrice(DateArray)
43 | strikeChoice = returnChoice()
44 | opt = yf.Ticker(ticker).option_chain(strikeChoice)
45 | print("Strike Choice was: " + strikeChoice)
46 |
47 |
48 | def pickAStrike2():
49 | global strikeChoice, opt
50 | strikeChoice = DateArray[2]
51 | opt = yf.Ticker(ticker).option_chain(strikeChoice)
52 | print("Strike Choice was: " + strikeChoice)
53 |
54 |
55 | def sortCallsandPuts():
56 | global calls, puts, opt
57 | calls = opt.calls
58 | calls = cleaner(calls)
59 | calls['Mid Price'] = calls.apply(lambda row: (row.ask + row.bid)/2, axis=1)
60 | calls = calls.drop(columns=["ask", "bid"])
61 | calls = calls[["strike", "Mid Price", "openInterest", "volume"]]
62 |
63 | puts = opt.puts
64 | puts = cleaner(puts)
65 | puts['Mid Price'] = puts.apply(lambda row: (row.ask + row.bid)/2, axis=1)
66 | puts = puts.drop(columns=["ask", "bid"])
67 | puts = puts[["strike", "Mid Price", "openInterest", "volume"]]
68 |
69 |
70 | def cleaner(object):
71 | return object.drop(columns=["contractSymbol", "lastTradeDate", "lastPrice",
72 | "change", "percentChange", "impliedVolatility", "inTheMoney", "contractSize", "currency"])
73 |
74 |
75 | def heatCleaner(object):
76 | object = object.drop(columns=["contractSymbol", "lastTradeDate", "lastPrice",
77 | "change", "percentChange", "volume", "inTheMoney", "contractSize", "currency", "impliedVolatility", "ask", "bid"])
78 | object = object[["strike", "openInterest"]]
79 | return object
80 |
81 |
82 | def heatCleanerVOLUME(object):
83 | object = object.drop(columns=["contractSymbol", "lastTradeDate", "lastPrice",
84 | "change", "percentChange", "openInterest", "inTheMoney", "contractSize", "currency", "impliedVolatility", "ask", "bid"])
85 | object = object[["strike", "volume"]]
86 | return object
87 |
88 |
89 | def getCalls():
90 | print("****************** Calls *********************")
91 | print(calls)
92 |
93 |
94 | def getPuts():
95 | print("****************** Puts *********************")
96 | print(puts)
97 |
98 |
99 | def displayOptionsChain():
100 | print("Length of the Chain: " + str(len(DateArray)) + "\n")
101 | print("\n".join(DateArray))
102 |
103 |
104 | def displayCleanOptionChain():
105 | print("****************** Calls ********************** ---[" + ticker.upper(
106 | )+"]--- ******************** Puts ********************* ")
107 | temp = {' ': [""]}
108 | tempPD = pd.DataFrame(data=temp)
109 | merged = pd.concat([calls, tempPD, puts], axis=1)
110 | merged = merged.fillna("")
111 | # merged.style.hide_index()
112 | print(merged.to_string(index=False))
113 | ############
114 |
115 |
116 | def OIChart():
117 | sortCallsandPuts()
118 | callData = calls.drop(columns=['Mid Price', 'volume'])
119 | putData = puts.drop(columns=['Mid Price', 'volume'])
120 | finalFrame = pd.DataFrame(callData)
121 | finalFrame.rename(columns={'openInterest': 'Calls'}, inplace=True)
122 | tempFrame = pd.DataFrame(putData)
123 |
124 | tempFrame.rename(columns={'openInterest': 'Puts'}, inplace=True)
125 | # finalFrame = pd.concat([finalFrame, tempFrame])
126 | finalFrame = pd.merge(finalFrame, tempFrame, on='strike')
127 | finalFrame.plot.bar(figsize=(20, 8), x="strike", y=["Calls", "Puts"],
128 | title="Open Interest for "+ticker.upper()+" all options at every strike on "+strikeChoice)
129 |
130 | ####################
131 | # fig = plt.figure()
132 | # a = ScrollableWindow(fig)
133 | plt.savefig("out.png")
134 | plt.clf()
135 | img = Image.open('out.png')
136 | img.show()
137 | # plt.show(block=True)
138 | print("***********************")
139 |
140 | # dataframe place NaN with 0
141 |
142 |
143 | def CallsOIMap(): # plt.style.use("dark_background")
144 | callsArray = heatCleaner(opt.calls)
145 | callsArray.rename(columns={'openInterest': DateArray[1]}, inplace=True)
146 | for x in range(2, len(DateArray)-1):
147 | opt2 = yf.Ticker(ticker).option_chain(DateArray[x])
148 | callsArray2 = heatCleaner(opt2.calls)
149 | callsArray2.rename(
150 | columns={'openInterest': DateArray[x]}, inplace=True)
151 | callsArray = pd.merge(callsArray, callsArray2, on='strike')
152 | callsArray.set_index('strike', inplace=True)
153 | callsArray = callsArray.fillna(0)
154 | print(callsArray)
155 | heat_map = sb.heatmap(callsArray, cmap="Reds", linewidths=0)
156 | global ArrayStore
157 | ArrayStore = callsArray
158 | plt.yticks(rotation=0)
159 | plt.xticks(rotation=50)
160 | plt.gca().invert_yaxis()
161 | plt.show()
162 | plt.clf()
163 |
164 |
165 | def PutsOIMap(): # plt.style.use("dark_background")
166 | callsArray = heatCleaner(opt.puts)
167 | callsArray.rename(columns={'openInterest': DateArray[1]}, inplace=True)
168 | for x in range(2, len(DateArray)-1):
169 | opt2 = yf.Ticker(ticker).option_chain(DateArray[x])
170 | callsArray2 = heatCleaner(opt2.puts)
171 | callsArray2.rename(
172 | columns={'openInterest': DateArray[x]}, inplace=True)
173 | callsArray = pd.merge(callsArray, callsArray2, on='strike')
174 | callsArray.set_index('strike', inplace=True)
175 | callsArray = callsArray.fillna(0)
176 | print(callsArray)
177 | heat_map = sb.heatmap(callsArray, cmap="Blues", linewidths=0)
178 | global ArrayStore
179 | ArrayStore = callsArray
180 | plt.yticks(rotation=0)
181 | plt.xticks(rotation=50)
182 | plt.gca().invert_yaxis()
183 | plt.show()
184 | plt.clf()
185 |
186 |
187 | def CallsVolumeMap():
188 | callsArray = heatCleanerVOLUME(opt.calls)
189 | callsArray.rename(columns={'volume': DateArray[1]}, inplace=True)
190 | for x in range(2, len(DateArray)-1):
191 | opt2 = yf.Ticker(ticker).option_chain(DateArray[x])
192 | callsArray2 = heatCleanerVOLUME(opt2.calls)
193 | callsArray2.rename(
194 | columns={'volume': DateArray[x]}, inplace=True)
195 | callsArray = pd.merge(callsArray, callsArray2, on='strike')
196 | callsArray.set_index('strike', inplace=True)
197 | callsArray = callsArray.fillna(0)
198 | print(callsArray)
199 | heat_map = sb.heatmap(callsArray, cmap="Reds", linewidths=0)
200 | global ArrayStore
201 | ArrayStore = callsArray
202 | plt.yticks(rotation=0)
203 | plt.xticks(rotation=50)
204 | plt.gca().invert_yaxis()
205 | plt.show()
206 | plt.clf()
207 |
208 |
209 | def PutsVolumeMap():
210 | callsArray = heatCleanerVOLUME(opt.puts)
211 | callsArray.rename(columns={'volume': DateArray[1]}, inplace=True)
212 | for x in range(2, len(DateArray)-1):
213 | opt2 = yf.Ticker(ticker).option_chain(DateArray[x])
214 | callsArray2 = heatCleanerVOLUME(opt2.puts)
215 | callsArray2.rename(
216 | columns={'volume': DateArray[x]}, inplace=True)
217 | callsArray = pd.merge(callsArray, callsArray2, on='strike')
218 | callsArray.set_index('strike', inplace=True)
219 | callsArray = callsArray.fillna(0)
220 | print(callsArray)
221 | heat_map = sb.heatmap(callsArray, cmap="Blues", linewidths=0)
222 | global ArrayStore
223 | ArrayStore = callsArray
224 | plt.yticks(rotation=0)
225 | plt.xticks(rotation=50)
226 | plt.gca().invert_yaxis()
227 | plt.show()
228 | plt.clf()
229 |
230 |
231 | def threedeegraph(object):
232 |
233 | eg = object
234 | # thickness of the bars
235 | dx, dy = .8, .8
236 | # prepare 3d axes
237 | fig = plt.figure(figsize=(10, 6))
238 | ax = Axes3D(fig)
239 | # set up positions for the bars
240 | xpos = np.arange(eg.shape[0])
241 | ypos = np.arange(eg.shape[1])
242 | # set the ticks in the middle of the bars
243 | ax.set_xticks(xpos + dx/2)
244 | ax.set_yticks(ypos + dy/2)
245 | # create meshgrid
246 | # print xpos before and after this block if not clear
247 | xpos, ypos = np.meshgrid(xpos, ypos)
248 | xpos = xpos.flatten()
249 | ypos = ypos.flatten()
250 | # the bars starts from 0 attitude
251 | zpos = np.zeros(eg.shape).flatten()
252 | # the bars' heights
253 | dz = eg.values.ravel(order='F')
254 | # plot and color
255 | values = np.linspace(0.2, 1., xpos.ravel().shape[0])
256 | colors = cm.rainbow(values)
257 | ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=colors)
258 | # put the column / index labels
259 | ax.w_yaxis.set_ticklabels(eg.columns)
260 | ax.w_xaxis.set_ticklabels(eg.index)
261 | # name the axes
262 | ax.set_xlabel('Strike')
263 | # ax.set_ylabel('Date')
264 | ax.set_zlabel('Open Interest / Volume')
265 | # fig.colorbar(surf1, ax=ax1, shrink=0.5, aspect=5)
266 | plt.show()
267 | plt.clf()
268 |
269 |
270 | def askForStrikePrice():
271 | pickStrikePrice(calls['strike'].astype(str).tolist())
272 |
273 |
274 | def setticker():
275 | askForTicker()
276 | getOptionsChain(ticker)
277 |
278 |
279 | def optionchainMENU():
280 | sortCallsandPuts()
281 | displayOptionsChain()
282 |
283 |
284 | def callOI3dmenu():
285 | CallsOIMap()
286 | threedeegraph(ArrayStore)
287 |
288 |
289 | def putOI3dmenu():
290 | PutsOIMap()
291 | threedeegraph(ArrayStore)
292 |
293 |
294 | def callVolumemenu():
295 | CallsVolumeMap()
296 | threedeegraph(ArrayStore)
297 |
298 |
299 | def putVolumemenu():
300 | PutsVolumeMap()
301 | threedeegraph(ArrayStore)
302 |
303 |
304 | def askForTickerMENU():
305 | askForTicker()
306 | getOptionsChain(ticker)
307 | pickAStrike2()
308 |
309 |
310 | def mainMENUswitch():
311 | def switchBoard(arguement):
312 | switcher = {
313 | "setTicker": lambda: askForTickerMENU(),
314 | "setStrike": lambda: pickAStrike(),
315 | "optionChain": lambda: optionchainMENU(),
316 | "OImage": lambda: OIChart(),
317 | "CallVolumeMap": lambda: CallsVolumeMap(),
318 | "CallOIMap": lambda: CallsOIMap(),
319 | "PutVolumeMap": lambda: PutsVolumeMap(),
320 | "PutsOIMap": lambda: PutsOIMap(),
321 | "CallVolume3D": lambda: callVolumemenu(),
322 | "CallOI3D": lambda: callOI3dmenu(),
323 | "PutVolume3D": lambda: putVolumemenu(),
324 | "PutOI3D": lambda: putOI3dmenu(),
325 | "exit": lambda: sys.exit,
326 | }
327 | return switcher.get(arguement, lambda: "error")()
328 |
329 | print("******* \n Welcome to Sam's Option Scanner \n *******")
330 | while(True):
331 | print("PICK ONE: setticker, setstrike, optionchain, OIimage, CallVolumeMap, CallOIMap, PutVolumeMap, PutOIMap, CallVolume3D, CallOI3D, PutVolume3D, PutOI3D, exit")
332 | choice = input()
333 | switchBoard(choice)()
334 |
335 |
336 | def mainMENUnested():
337 | def repeat():
338 | print("PICK ONE: setticker, setstrike, optionchain, OIimage, CallsVolumeMap, CallsOIMap, PutsVolumeMap, PutsOIMap, CallVolume3D, CallOI3D, PutVolume3D, PutOI3D, exit")
339 | choice = input()
340 |
341 | if choice == "setticker":
342 | # askForTicker() # get ticker of choice from user
343 | # getOptionsChain(ticker) # get entire option chain from yFinance
344 | askForTickerMENU()
345 | repeat()
346 | elif choice == "setstrike":
347 | pickAStrike() # asks user for specific date
348 | # askForStrikePrice() # prompts user to choose a strike from the table
349 | # displayOptionsChain() #show entire option chain
350 | repeat()
351 | elif choice == "optionchain":
352 | sortCallsandPuts() # breaks options chain into essential data and sorts by calls / puts
353 | displayCleanOptionChain() # displays calls and puts as a clean table
354 | repeat()
355 | elif choice == "OIimage":
356 | OIChart()
357 | repeat()
358 | elif choice == "CallsVolumeMap":
359 | CallsVolumeMap()
360 | repeat()
361 | elif choice == "CallsOIMap":
362 | CallsOIMap()
363 | repeat()
364 | elif choice == "PutsVolumeMap":
365 | PutsVolumeMap()
366 | repeat()
367 | elif choice == "PutsOIMap":
368 | PutsOIMap()
369 | repeat()
370 | elif choice == "CallVolume3D":
371 | CallsVolumeMap()
372 | threedeegraph(ArrayStore)
373 | repeat()
374 | elif choice == "CallOI3D":
375 | CallsOIMap()
376 | threedeegraph(ArrayStore)
377 | repeat()
378 | elif choice == "PutVolume3D":
379 | PutsVolumeMap()
380 | threedeegraph(ArrayStore)
381 | repeat()
382 | elif choice == "PutOI3D":
383 | PutsOIMap()
384 | threedeegraph(ArrayStore)
385 | repeat()
386 | elif choice == "exit":
387 | sys.exit()
388 | else:
389 | print("unexpected choice")
390 | repeat()
391 |
392 | print("******* \n Welcome to Sam's Option Scanner \n *******")
393 | repeat()
394 |
395 |
396 | # mainMENUswitch()
397 | mainMENUnested()
398 | print("All done")
399 |
--------------------------------------------------------------------------------
/src/scrolly.py:
--------------------------------------------------------------------------------
1 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
2 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
3 | from PyQt5 import QtWidgets
4 | import matplotlib.pyplot as plt
5 | import matplotlib
6 | # Make sure that we are using QT5
7 | matplotlib.use('Qt5Agg')
8 |
9 |
10 | class ScrollableWindow(QtWidgets.QMainWindow):
11 | def __init__(self, fig):
12 | self.qapp = QtWidgets.QApplication([])
13 |
14 | QtWidgets.QMainWindow.__init__(self)
15 | self.widget = QtWidgets.QWidget()
16 | self.setCentralWidget(self.widget)
17 | self.widget.setLayout(QtWidgets.QVBoxLayout())
18 | self.widget.layout().setContentsMargins(0, 0, 0, 0)
19 | self.widget.layout().setSpacing(0)
20 |
21 | self.fig = fig
22 | self.canvas = FigureCanvas(self.fig)
23 | self.canvas.draw()
24 | self.scroll = QtWidgets.QScrollArea(self.widget)
25 | self.scroll.setWidget(self.canvas)
26 |
27 | self.nav = NavigationToolbar(self.canvas, self.widget)
28 | self.widget.layout().addWidget(self.nav)
29 | self.widget.layout().addWidget(self.scroll)
30 |
31 | self.show()
32 | exit(self.qapp.exec_())
33 |
34 |
35 | def main():
36 | # create a figure and some subplots
37 | fig, axes = plt.subplots(ncols=4, nrows=5, figsize=(16, 16))
38 | for ax in axes.flatten():
39 | ax.plot([2, 3, 5, 1])
40 |
41 | # pass the figure to the custom window
42 | a = ScrollableWindow(fig)
43 |
--------------------------------------------------------------------------------