From b146a93ddbde00b7f069ec144ca58edbefbf6b93 Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Sun, 8 Sep 2024 16:11:57 +0200 Subject: [PATCH] add otm percentage --- app/cron_options_gex.py | 93 +++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 22 deletions(-) diff --git a/app/cron_options_gex.py b/app/cron_options_gex.py index 69437cf..b49652d 100644 --- a/app/cron_options_gex.py +++ b/app/cron_options_gex.py @@ -83,15 +83,35 @@ def compute_daily_gex(option_data_list, volatility): daily_gex['date'] = daily_gex['date'].astype(str) return daily_gex -def summarize_option_chain(option_data_list): +def calculate_otm_percentage(option_data_list): + otm_count = 0 + total_options = len(option_data_list) + + for option_data in option_data_list: + strike_price = float(option_data['strike_price']) + put_call = option_data['put_call'] + stock_price = float(option_data['stock_price']) # Get stock price for this option + + # Check if the option is out-of-the-money + if (put_call == 'CALL' and strike_price > stock_price) or (put_call == 'PUT' and strike_price < stock_price): + otm_count += 1 + + if total_options > 0: + return (otm_count / total_options) * 100 + else: + return 0 + + +def summarize_option_chain_with_otm(option_data_list, df_price): summary_data = [] for option_data in option_data_list: try: date = datetime.strptime(option_data['date'], "%Y-%m-%d").date() + expiration_date = datetime.strptime(option_data['date_expiration'], "%Y-%m-%d").date() + open_interest = int(option_data.get('open_interest', 0)) volume = int(option_data.get('volume', 0)) - price = float(option_data.get('price', 0)) strike_price = float(option_data.get('strike_price', 0)) put_call = option_data.get('put_call', 'CALL') sentiment = option_data.get('sentiment', 'NEUTRAL') @@ -102,6 +122,17 @@ def summarize_option_chain(option_data_list): except (TypeError, ValueError): premium = 0 + # Determine the stock price based on expiration date + if expiration_date > date.today(): + stock_price = df_price['close'].iloc[-1] # Latest stock price + else: + # Get the stock price on the option's date + stock_price_row = df_price[df_price['date'] == str(date)] + if not stock_price_row.empty: + stock_price = stock_price_row['close'].values[0] + else: + continue # Skip this option if the price isn't available for the date + # Calculate Bull/Bear/Neutral premiums based on sentiment if sentiment == 'BULLISH': bull_premium = premium @@ -116,6 +147,7 @@ def summarize_option_chain(option_data_list): bear_premium = 0 neutral_premium = premium + # Append option data for later summarization summary_data.append({ 'date': date, 'open_interest': open_interest, @@ -123,38 +155,54 @@ def summarize_option_chain(option_data_list): 'p_vol': volume if put_call == 'PUT' else 0, 'bull_premium': bull_premium, 'bear_premium': bear_premium, - 'neutral_premium': neutral_premium + 'neutral_premium': neutral_premium, + 'put_call': put_call, + 'strike_price': strike_price, + 'stock_price': stock_price }) - except: - pass + + except Exception as e: + print(f"Error processing option data: {e}") + continue # Summarize by date df_summary = pd.DataFrame(summary_data) + + # Calculate OTM percentage for each day + def calculate_daily_otm(df): + return calculate_otm_percentage(df.to_dict('records')) # Pass the day's options for OTM calculation + + # Apply OTM percentage calculation for each day daily_summary = df_summary.groupby('date').agg( total_oi=('open_interest', 'sum'), total_bull_prem=('bull_premium', 'sum'), total_bear_prem=('bear_premium', 'sum'), total_neutral_prem=('neutral_premium', 'sum'), c_vol=('c_vol', 'sum'), - p_vol=('p_vol', 'sum') + p_vol=('p_vol', 'sum'), ).reset_index() - # Calculate Bull/Bear ratio - - try: - daily_summary['bear_ratio'] = round(daily_summary['total_bear_prem'] / (daily_summary['total_bull_prem']+daily_summary['total_bear_prem']+daily_summary['total_neutral_prem']) * 100, 2) - daily_summary['bull_ratio'] = round(daily_summary['total_bull_prem'] / (daily_summary['total_bull_prem']+daily_summary['total_bear_prem']+daily_summary['total_neutral_prem']) * 100, 2) - daily_summary['neutral_ratio'] = round(daily_summary['total_neutral_prem'] / (daily_summary['total_bull_prem']+daily_summary['total_bear_prem']+daily_summary['total_neutral_prem']) * 100, 2) - except: - daily_summary['bear_ratio'] = None - daily_summary['bull_ratio'] = None - daily_summary['neutral_ratio'] = None - + # Calculate OTM percentage for each date and assign it to the daily_summary + daily_summary['otm_ratio'] = df_summary.groupby('date').apply(lambda df: round(calculate_otm_percentage(df.to_dict('records')), 1)).values - daily_summary['total_volume'] = round(daily_summary['c_vol'] + daily_summary['p_vol'],2) - daily_summary['total_neutral_prem'] = round(daily_summary['total_neutral_prem'],2) + + # Calculate Bull/Bear/Neutral ratios + try: + total_prem = daily_summary['total_bull_prem'] + daily_summary['total_bear_prem'] + daily_summary['total_neutral_prem'] + daily_summary['bull_ratio'] = round(daily_summary['total_bull_prem'] / total_prem * 100, 2) + daily_summary['bear_ratio'] = round(daily_summary['total_bear_prem'] / total_prem * 100, 2) + daily_summary['neutral_ratio'] = round(daily_summary['total_neutral_prem'] / total_prem * 100, 2) + except ZeroDivisionError: + daily_summary['bull_ratio'] = None + daily_summary['bear_ratio'] = None + daily_summary['neutral_ratio'] = None + + # Calculate total volume (call + put) and format other fields + daily_summary['total_volume'] = round(daily_summary['c_vol'] + daily_summary['p_vol'], 2) + daily_summary['total_neutral_prem'] = round(daily_summary['total_neutral_prem'], 2) daily_summary['date'] = daily_summary['date'].astype(str) daily_summary = daily_summary.sort_values(by='date', ascending=False) + # Return the summarized dataframe return daily_summary @@ -203,7 +251,7 @@ query_template = """ """ # Process each symbol -for ticker in total_symbols: +for ticker in ['GME']: #total_symbols: try: query = query_template.format(ticker=ticker) df_price = pd.read_sql_query(query, stock_con if ticker in stock_symbols else etf_con, params=(start_date_str, end_date_str)).round(2) @@ -212,11 +260,12 @@ for ticker in total_symbols: volatility = calculate_volatility(df_price) ticker_data = get_data(ticker) - daily_option_chain = summarize_option_chain(ticker_data) + daily_option_chain = summarize_option_chain_with_otm(ticker_data, df_price) daily_option_chain = daily_option_chain.merge(df_price[['date', 'changesPercentage']], on='date', how='inner') if not daily_option_chain.empty: save_json(ticker, daily_option_chain.to_dict('records'), 'json/options-chain/companies') - + + daily_gex = compute_daily_gex(ticker_data, volatility) daily_gex = daily_gex.merge(df_price[['date', 'close']], on='date', how='inner') if not daily_gex.empty: