├── Example.xlsx ├── Photos ├── 1.png └── 2.png ├── SPYChain.xlsx ├── LICENSE ├── README.md ├── Straddle_Master.py └── Straddle_Masterr.py /Example.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Connor9994/Straddle-Master-Stocks/HEAD/Example.xlsx -------------------------------------------------------------------------------- /Photos/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Connor9994/Straddle-Master-Stocks/HEAD/Photos/1.png -------------------------------------------------------------------------------- /Photos/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Connor9994/Straddle-Master-Stocks/HEAD/Photos/2.png -------------------------------------------------------------------------------- /SPYChain.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Connor9994/Straddle-Master-Stocks/HEAD/SPYChain.xlsx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Straddle-Master-Stocks 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/Connor9994/Straddle-Master-Stocks?style=social) ![GitHub forks](https://img.shields.io/github/forks/Connor9994/Straddle-Master-Stocks?style=social) ![GitHub issues](https://img.shields.io/github/issues/Connor9994/Straddle-Master-Stocks) 4 | 5 | ![1](https://github.com/Connor9994/Straddle-Master-Stocks/blob/main/Photos/1.png) 6 | 7 | ## Overview 8 | 9 | This Python script analyzes SPY (SPDR S&P 500 ETF Trust) options data to calculate and export straddle and strangle strategies for various expiration dates. The tool fetches real-time options data from Yahoo Finance and generates a comprehensive Excel workbook with detailed analysis. 10 | 11 | ## Features 12 | 13 | - **Complete Options Chain Data**: Retrieves 100% of available SPY call and put options 14 | - **Current Price Analysis**: Automatically detects the current SPY price and rounds to nearest strike price 15 | - **Strategy Analysis**: Calculates straddle and strangle strategies with break-even points 16 | - **Multi-Sheet Excel Export**: Creates organized Excel workbook with separate sheets for each expiration date 17 | - **Risk Analysis**: Computes break-even ranges and profit/loss spans for various option combinations 18 | 19 | ## How It Works 20 | 21 | ### 1. Data Collection 22 | - Fetches all available SPY options expiration dates 23 | - Retrieves both call and put options for each expiration 24 | - Calculates midpoint prices (mark) from bid-ask spreads 25 | 26 | ### 2. Core Analysis 27 | - **Straddle Analysis**: Analyzes options with the same strike price (±10 points from current price) 28 | - **Strangle Analysis**: Analyzes out-of-the-money call and put combinations 29 | - **Break-even Calculation**: Computes upper and lower break-even points for each strategy 30 | 31 | ### 3. Output Structure 32 | The generated Excel file contains: 33 | - **SPY_Chain Sheet**: Complete raw options data 34 | - **Date-Specific Sheets**: For each expiration date, includes: 35 | - Straddle analysis for strikes around current price 36 | - Strangle analysis for various strike combinations 37 | - Break-even ranges and span calculations 38 | 39 | ## Key Metrics Calculated 40 | 41 | - **Break-even Up**: Upper break-even price (strike + total premium) 42 | - **Break-even Down**: Lower break-even price (strike - total premium) 43 | - **Span**: Difference between break-even points (profit range width) 44 | 45 | ## Usage 46 | 47 | Simply run the script to: 48 | 1. Fetch current SPY options data 49 | 2. Analyze straddle and strangle strategies 50 | 3. Generate `SPYChain.xlsx` with comprehensive analysis 51 | 52 | ## Dependencies 53 | 54 | - `pandas` - Data manipulation and Excel export 55 | - `numpy` - Numerical operations and filtering 56 | - `yfinance` - Yahoo Finance API for options data 57 | - `openpyxl` - Excel file handling 58 | - `datetime` - Date processing utilities 59 | 60 | ![2](https://github.com/Connor9994/Straddle-Master-Stocks/blob/main/Photos/2.png) 61 | 62 | ## Output 63 | 64 | The script creates an Excel file with organized sheets showing options strategies, making it easy to identify potential trading opportunities based on break-even analysis and price movement expectations. 65 | 66 | ## Note 67 | 68 | This tool is designed for educational and analytical purposes. Options trading involves significant risk and may not be suitable for all investors. Always conduct thorough research and consider consulting with a financial professional before engaging in options trading. 69 | -------------------------------------------------------------------------------- /Straddle_Master.py: -------------------------------------------------------------------------------- 1 | import pandas as pd #Import Pandas for Datasets 2 | import numpy as np #Import Numpy for Dataset Filters 3 | import yfinance as yf #Import Yahoo Finance for Stock Info 4 | import datetime #Import Datetime for dates in Stock Info 5 | import openpyxl #Import OpenPYXL to save Excel Files 6 | 7 | #Function to obtain 100% of Options Data for a Ticker 8 | def options_chain(symbol): 9 | tk = yf.Ticker(symbol) 10 | # Expiration dates 11 | exps = tk.options 12 | 13 | # Get options for each expiration 14 | options = pd.DataFrame() 15 | for e in exps: 16 | opt = tk.option_chain(e) 17 | opt = pd.DataFrame().append(opt.calls).append(opt.puts) 18 | opt['expirationDate'] = e 19 | options = options.append(opt, ignore_index=True) 20 | 21 | # Bizarre error in yfinance that gives the wrong expiration date 22 | # Add 1 day to get the correct expiration date 23 | options['expirationDate'] = pd.to_datetime(options['expirationDate']) #+ datetime.timedelta(days = 1) 24 | options['dte'] = (options['expirationDate'] - datetime.datetime.today()).dt.days / 365 25 | 26 | # Boolean column if the option is a CALL 27 | options['CALL'] = options['contractSymbol'].str[4:].apply( 28 | lambda x: "C" in x) 29 | 30 | options[['bid', 'ask', 'strike']] = options[['bid', 'ask', 'strike']].apply(pd.to_numeric) 31 | options['mark'] = (options['bid'] + options['ask']) / 2 # Calculate the midpoint of the bid-ask 32 | 33 | # Drop unnecessary and meaningless columns 34 | options = options.drop(columns = ['contractSize', 'currency', 'change', 'percentChange', 'lastTradeDate', 'lastPrice','dte','inTheMoney','impliedVolatility','volume']) 35 | options = options.drop(columns = ['contractSymbol']) 36 | return options 37 | 38 | #Options Data to Excel 39 | SPYChain = options_chain("SPY") 40 | filepath = r"C:\Users\Test\Desktop\Straddle Master\SPYChain.xlsx" 41 | #print(SPYChain) 42 | 43 | #Get Price of SPY Currently (Rounded to nearest Strike Price) 44 | SPY = yf.Ticker("SPY") 45 | data = SPY.history() 46 | last_quote = round(data.tail(1)['Close'].iloc[0]) 47 | print("SPY",last_quote) 48 | 49 | CallValue = np.where((SPYChain['CALL']==True) & (SPYChain['strike']== last_quote)) #Get List of Dates 50 | ExpirationDates = SPYChain['expirationDate'].loc[CallValue] 51 | SmolData= pd.DataFrame(["1","2","3","4","5","6","7"]) 52 | 53 | writer = pd.ExcelWriter('SPYChain.xlsx') #Open Writer of Excel File 54 | SPYChain.to_excel(writer, sheet_name='SPY_Chain', index=False) #Add All Data to Sheet 1 55 | for date in ExpirationDates: 56 | DateFormatted = date.strftime("%Y-%m-%d") 57 | print(DateFormatted) 58 | Data = pd.DataFrame() 59 | Data['min'] = '' 60 | Data['span'] = '' 61 | Data['max'] = '' 62 | #Straddles surrounding middle 63 | for strike in range(last_quote-10,last_quote+10): 64 | CallValueLoc = np.where((SPYChain['CALL'] == True) & (SPYChain['strike'] == strike) & (SPYChain['expirationDate'] == date)) 65 | PutValueLoc = np.where((SPYChain['CALL'] == False) & (SPYChain['strike'] == strike) & (SPYChain['expirationDate'] == date)) 66 | Call = SPYChain.loc[CallValueLoc] 67 | Put = SPYChain.loc[PutValueLoc] 68 | Call = Call.reset_index(drop=True) 69 | Put = Put.reset_index(drop=True) 70 | if ((Call.size> 0) & (Put.size>0)): 71 | BreakEvenUp = Call.at[0,'mark'] + Put.at[0,'mark'] + strike 72 | BreakEvenDown = strike - Call.at[0,'mark'] - Put.at[0,'mark'] 73 | Span = BreakEvenUp - BreakEvenDown 74 | Data = Data.append({'max' : BreakEvenUp,'span' : Span,'min' : BreakEvenDown}, ignore_index=True) 75 | Data = Data.append(Call) 76 | Data = Data.append(Put) 77 | 78 | Data = Data.append({'max' : "-",'span' : "-",'min' : "-",'strike' : "-",'bid' : "-",'ask' : "-",'openInterest' :"-",'expirationDate' :"-",'CALL' :"-",'mark' :"-"}, ignore_index=True) 79 | Data = Data.append({'max' : "@@@@@",'span' : "@@@@@",'min' : "@@@@@",'strike' : "@@@@@",'bid' : "@@@@@",'ask' : "@@@@@",'openInterest' :"@@@@@",'expirationDate' :"@@@@@",'CALL' :"@@@@@",'mark' :"@@@@@"}, ignore_index=True) 80 | Data = Data.append({'max' : "-",'span' : "-",'min' : "-",'strike' : "-",'bid' : "-",'ask' : "-",'openInterest' :"-",'expirationDate' :"-",'CALL' :"-",'mark' :"-"}, ignore_index=True) 81 | 82 | #Strangles surrounding middle 83 | for strike1 in range(last_quote,last_quote+10): 84 | for strike2 in range(last_quote-10,last_quote): 85 | CallValueLoc = np.where((SPYChain['CALL'] == True) & (SPYChain['strike'] == strike1) & (SPYChain['expirationDate'] == date)) 86 | PutValueLoc = np.where((SPYChain['CALL'] == False) & (SPYChain['strike'] == strike2) & (SPYChain['expirationDate'] == date)) 87 | Call = SPYChain.loc[CallValueLoc] 88 | Put = SPYChain.loc[PutValueLoc] 89 | Call = Call.reset_index(drop=True) 90 | Put = Put.reset_index(drop=True) 91 | if ((Call.size> 0) & (Put.size>0)): 92 | BreakEvenUp = Call.at[0,'mark'] + Put.at[0,'mark'] + strike1 93 | BreakEvenDown = strike2 - Call.at[0,'mark'] - Put.at[0,'mark'] 94 | Span = BreakEvenUp - BreakEvenDown 95 | Data = Data.append({'max' : BreakEvenUp,'span' : Span,'min' : BreakEvenDown}, ignore_index=True) 96 | Data = Data.append(Call) 97 | Data = Data.append(Put) 98 | Data.to_excel(writer,sheet_name=DateFormatted, index=False) 99 | writer.save() 100 | 101 | 102 | 103 | #for i in range(last_quote-5,last_quote+5): 104 | # StrikePrice = i 105 | # CallValue = np.where((SPYChain['CALL']==True) & (SPYChain['strike']== StrikePrice)) 106 | # wb.create_sheet("Date") 107 | # print(SPYChain.loc[CallValue]) 108 | #wb.save(filepath) -------------------------------------------------------------------------------- /Straddle_Masterr.py: -------------------------------------------------------------------------------- 1 | import pandas as pd #Import Pandas for Datasets 2 | import numpy as np #Import Numpy for Dataset Filters 3 | import yfinance as yf #Import Yahoo Finance for Stock Info 4 | import datetime #Import Datetime for dates in Stock Info 5 | import openpyxl #Import OpenPYXL to save Excel Files 6 | 7 | #Function to obtain 100% of Options Data for a Ticker 8 | def options_chain(symbol): 9 | tk = yf.Ticker(symbol) 10 | # Expiration dates 11 | exps = tk.options 12 | 13 | # Get options for each expiration 14 | options = pd.DataFrame() 15 | for e in exps: 16 | opt = tk.option_chain(e) 17 | opt = pd.DataFrame().append(opt.calls).append(opt.puts) 18 | opt['expirationDate'] = e 19 | options = options.append(opt, ignore_index=True) 20 | 21 | # Bizarre error in yfinance that gives the wrong expiration date 22 | # Add 1 day to get the correct expiration date 23 | options['expirationDate'] = pd.to_datetime(options['expirationDate']) #+ datetime.timedelta(days = 1) 24 | options['dte'] = (options['expirationDate'] - datetime.datetime.today()).dt.days / 365 25 | 26 | # Boolean column if the option is a CALL 27 | options['CALL'] = options['contractSymbol'].str[4:].apply( 28 | lambda x: "C" in x) 29 | 30 | options[['bid', 'ask', 'strike']] = options[['bid', 'ask', 'strike']].apply(pd.to_numeric) 31 | options['mark'] = (options['bid'] + options['ask']) / 2 # Calculate the midpoint of the bid-ask 32 | 33 | # Drop unnecessary and meaningless columns 34 | options = options.drop(columns = ['contractSize', 'currency', 'change', 'percentChange', 'lastTradeDate', 'lastPrice','dte','inTheMoney','impliedVolatility','volume']) 35 | options = options.drop(columns = ['contractSymbol']) 36 | return options 37 | 38 | #Options Data to Excel 39 | SPYChain = options_chain("SPY") 40 | filepath = r"C:\Users\Test\Desktop\Straddle Master\SPYChain.xlsx" 41 | #print(SPYChain) 42 | 43 | #Get Price of SPY Currently (Rounded to nearest Strike Price) 44 | SPY = yf.Ticker("SPY") 45 | data = SPY.history() 46 | last_quote = round(data.tail(1)['Close'].iloc[0]) 47 | print("SPY",last_quote) 48 | 49 | CallValue = np.where((SPYChain['CALL']==True) & (SPYChain['strike']== last_quote)) #Get List of Dates 50 | ExpirationDates = SPYChain['expirationDate'].loc[CallValue] 51 | SmolData= pd.DataFrame(["1","2","3","4","5","6","7"]) 52 | 53 | writer = pd.ExcelWriter('SPYChain.xlsx') #Open Writer of Excel File 54 | SPYChain.to_excel(writer, sheet_name='SPY_Chain', index=False) #Add All Data to Sheet 1 55 | for date in ExpirationDates: 56 | DateFormatted = date.strftime("%Y-%m-%d") 57 | print(DateFormatted) 58 | Data = pd.DataFrame() 59 | Data['min'] = '' 60 | Data['span'] = '' 61 | Data['max'] = '' 62 | #Straddles surrounding middle 63 | for strike in range(last_quote-10,last_quote+10): 64 | CallValueLoc = np.where((SPYChain['CALL'] == True) & (SPYChain['strike'] == strike) & (SPYChain['expirationDate'] == date)) 65 | PutValueLoc = np.where((SPYChain['CALL'] == False) & (SPYChain['strike'] == strike) & (SPYChain['expirationDate'] == date)) 66 | Call = SPYChain.loc[CallValueLoc] 67 | Put = SPYChain.loc[PutValueLoc] 68 | Call = Call.reset_index(drop=True) 69 | Put = Put.reset_index(drop=True) 70 | if ((Call.size> 0) & (Put.size>0)): 71 | BreakEvenUp = Call.at[0,'mark'] + Put.at[0,'mark'] + strike 72 | BreakEvenDown = strike - Call.at[0,'mark'] - Put.at[0,'mark'] 73 | Span = BreakEvenUp - BreakEvenDown 74 | Data = Data.append({'max' : BreakEvenUp,'span' : Span,'min' : BreakEvenDown}, ignore_index=True) 75 | Data = Data.append(Call) 76 | Data = Data.append(Put) 77 | 78 | Data = Data.append({'max' : "-",'span' : "-",'min' : "-",'strike' : "-",'bid' : "-",'ask' : "-",'openInterest' :"-",'expirationDate' :"-",'CALL' :"-",'mark' :"-"}, ignore_index=True) 79 | Data = Data.append({'max' : "@@@@@",'span' : "@@@@@",'min' : "@@@@@",'strike' : "@@@@@",'bid' : "@@@@@",'ask' : "@@@@@",'openInterest' :"@@@@@",'expirationDate' :"@@@@@",'CALL' :"@@@@@",'mark' :"@@@@@"}, ignore_index=True) 80 | Data = Data.append({'max' : "-",'span' : "-",'min' : "-",'strike' : "-",'bid' : "-",'ask' : "-",'openInterest' :"-",'expirationDate' :"-",'CALL' :"-",'mark' :"-"}, ignore_index=True) 81 | 82 | #Strangles surrounding middle 83 | for strike1 in range(last_quote,last_quote+10): 84 | for strike2 in range(last_quote-10,last_quote): 85 | CallValueLoc = np.where((SPYChain['CALL'] == True) & (SPYChain['strike'] == strike1) & (SPYChain['expirationDate'] == date)) 86 | PutValueLoc = np.where((SPYChain['CALL'] == False) & (SPYChain['strike'] == strike2) & (SPYChain['expirationDate'] == date)) 87 | Call = SPYChain.loc[CallValueLoc] 88 | Put = SPYChain.loc[PutValueLoc] 89 | Call = Call.reset_index(drop=True) 90 | Put = Put.reset_index(drop=True) 91 | if ((Call.size> 0) & (Put.size>0)): 92 | BreakEvenUp = Call.at[0,'mark'] + Put.at[0,'mark'] + strike1 93 | BreakEvenDown = strike2 - Call.at[0,'mark'] - Put.at[0,'mark'] 94 | Span = BreakEvenUp - BreakEvenDown 95 | Data = Data.append({'max' : BreakEvenUp,'span' : Span,'min' : BreakEvenDown}, ignore_index=True) 96 | Data = Data.append(Call) 97 | Data = Data.append(Put) 98 | Data.to_excel(writer,sheet_name=DateFormatted, index=False) 99 | writer.save() 100 | 101 | 102 | 103 | #for i in range(last_quote-5,last_quote+5): 104 | # StrikePrice = i 105 | # CallValue = np.where((SPYChain['CALL']==True) & (SPYChain['strike']== StrikePrice)) 106 | # wb.create_sheet("Date") 107 | # print(SPYChain.loc[CallValue]) 108 | #wb.save(filepath) --------------------------------------------------------------------------------