change quarter date for hedge funds

This commit is contained in:
MuslemRahimi 2024-10-25 13:42:02 +02:00
parent 5985c54c77
commit b406a53f68
4 changed files with 126 additions and 110 deletions

View File

@ -62,7 +62,8 @@ crypto_con.close()
load_dotenv() load_dotenv()
api_key = os.getenv('FMP_API_KEY') api_key = os.getenv('FMP_API_KEY')
quarter_date = '2024-09-30' quarter_date = '2024-06-30'
if os.path.exists("backup_db/institute.db"): if os.path.exists("backup_db/institute.db"):

View File

@ -27,7 +27,7 @@ warnings.filterwarnings("ignore", category=RuntimeWarning, message="invalid valu
start_date = datetime(2015, 1, 1).strftime("%Y-%m-%d") start_date = datetime(2015, 1, 1).strftime("%Y-%m-%d")
end_date = datetime.today().strftime("%Y-%m-%d") end_date = datetime.today().strftime("%Y-%m-%d")
quarter_date = '2024-09-30' quarter_date = '2024-06-30'
if os.path.exists("backup_db/stocks.db"): if os.path.exists("backup_db/stocks.db"):

View File

@ -3937,7 +3937,7 @@ async def get_next_earnings(data:TickerData, api_key: str = Security(get_api_key
res = {} res = {}
redis_client.set(cache_key, orjson.dumps(res)) redis_client.set(cache_key, orjson.dumps(res))
redis_client.expire(cache_key,3600*3600) redis_client.expire(cache_key,5*60)
return res return res
@ -3955,7 +3955,7 @@ async def get_surprise_earnings(data:TickerData, api_key: str = Security(get_api
res = {} res = {}
redis_client.set(cache_key, orjson.dumps(res)) redis_client.set(cache_key, orjson.dumps(res))
redis_client.expire(cache_key,15*60) redis_client.expire(cache_key,5*60)
return res return res

View File

@ -2,6 +2,7 @@ import pandas as pd
import numpy as np import numpy as np
import ujson import ujson
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from scipy.stats import norm from scipy.stats import norm
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from benzinga import financial_data from benzinga import financial_data
@ -43,7 +44,7 @@ query_template = """
""" """
ticker = 'SPY' ticker = 'NVDA'
end_date = date.today() end_date = date.today()
start_date = end_date - timedelta(1) start_date = end_date - timedelta(1)
end_date_str = end_date.strftime('%Y-%m-%d') end_date_str = end_date.strftime('%Y-%m-%d')
@ -57,9 +58,6 @@ volatility = calculate_volatility(df_price)
print(start_date, end_date) print(start_date, end_date)
print('volatility', volatility) print('volatility', volatility)
stock_con.close()
etf_con.close()
def get_data(ticker): def get_data(ticker):
res_list = [] res_list = []
page = 0 page = 0
@ -89,32 +87,34 @@ print(len(ticker_data))
def calculate_option_greeks(S, K, T, r, sigma, option_type='CALL'): def calculate_option_greeks(S, K, T, r, sigma, option_type='CALL'):
""" """
Calculate option Greeks using Black-Scholes formula Calculate option Greeks using Black-Scholes formula with improved accuracy
S: Current stock price S: Current stock price
K: Strike price K: Strike price
T: Time to expiration (in years) T: Time to expiration (in years)
r: Risk-free rate r: Risk-free rate
sigma: Volatility sigma: Volatility
""" """
if T <= 0: if T <= 0 or sigma <= 0 or S <= 0:
return 0, 0 return 0, 0
d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)) try:
#d2 = d1 - sigma * np.sqrt(T) d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
if option_type == 'CALL':
delta = norm.cdf(d1)
#gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
else: # PUT
delta = norm.cdf(d1) - 1 #-norm.cdf(-d1)
#gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T)) if S > 0 and sigma > 0 and np.sqrt(T) > 0 else 0
return delta, gamma if option_type == 'CALL':
delta = norm.cdf(d1)
gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
else: # PUT
delta = -norm.cdf(-d1)
gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
return delta, gamma
except:
return 0, 0
def process_options_data(df): def process_options_data_by_expiry(df):
""" """
Process options data and calculate DEX and GEX Process options data with separate calculations for each expiration date
""" """
# Convert data types # Convert data types
df['strike_price'] = pd.to_numeric(df['strike_price']) df['strike_price'] = pd.to_numeric(df['strike_price'])
@ -123,14 +123,13 @@ def process_options_data(df):
df['underlying_price'] = pd.to_numeric(df['underlying_price']) df['underlying_price'] = pd.to_numeric(df['underlying_price'])
df['date_expiration'] = pd.to_datetime(df['date_expiration']) df['date_expiration'] = pd.to_datetime(df['date_expiration'])
df['date'] = pd.to_datetime(df['date']) df['date'] = pd.to_datetime(df['date'])
df['price'] = pd.to_numeric(df['price'])
# Calculate time to expiration in years # Calculate time to expiration in years
df['T'] = (df['date_expiration'] - df['date']).dt.days / 365.0 df['T'] = (df['date_expiration'] - df['date']).dt.days / 365.0
# Parameters for calculations # Use current risk-free rate
risk_free_rate = 0.05 # Current approximate risk-free rate risk_free_rate = 0.0525
# Calculate historical volatility (or you could use implied volatility if available)
sigma = volatility #df['underlying_price'].pct_change().std() * np.sqrt(252) if len(df) > 30 else 0.3
# Calculate Greeks for each option # Calculate Greeks for each option
greeks = df.apply(lambda row: calculate_option_greeks( greeks = df.apply(lambda row: calculate_option_greeks(
@ -138,114 +137,130 @@ def process_options_data(df):
row['strike_price'], row['strike_price'],
row['T'], row['T'],
risk_free_rate, risk_free_rate,
sigma, volatility,
row['put_call'] row['put_call']
), axis=1) ), axis=1)
df['delta'], df['gamma'] = zip(*greeks) df['delta'], df['gamma'] = zip(*greeks)
# Calculate DEX (Delta Exposure) and GEX (Gamma Exposure)
# Convert volume to float if it's not already
df['volume'] = df['volume'].astype(int)
df['open_interest'] = df['open_interest'].astype(float)
# Get price per contract
df['price'] = pd.to_numeric(df['price'])
# Calculate position values
contract_multiplier = 100 # Standard option contract multiplier
df['position_value'] = df['price'] * df['open_interest'] * contract_multiplier
# Calculate exposures # Calculate exposures
df['gex'] = df['gamma'] * df['volume'] * contract_multiplier #df['gamma'] * df['open_interest'] * df['volume'] * df['underlying_price'] contract_multiplier = 100
df['dex'] = df['delta'] * df['volume'] * contract_multiplier * df['underlying_price'] * 0.01 #df['delta'] * df['volume'] * df['underlying_price'] *0.01 df['delta_exposure'] = (df['delta'] * df['volume'] * contract_multiplier *
df['underlying_price'])
# Separate calls and puts
df['option_type'] = np.where(df['put_call'] == 'CALL', 'call', 'put')
return df return df
def plot_option_exposure(df, current_price): def plot_delta_exposure_by_expiry(df, current_price):
""" """
Create visualization of DEX and GEX profiles with focused y-axis Create a visualization similar to the screenshot with delta exposure by expiration date
""" """
plt.style.use('dark_background') plt.style.use('dark_background')
fig, ax = plt.subplots(figsize=(12, 8)) fig, ax = plt.subplots(figsize=(15, 12))
# Aggregate exposures by strike price # Create custom colormap for calls and puts
dex_by_strike = df.groupby('strike_price')['dex'].sum().reset_index() call_colors = ['#90EE90', '#32CD32', '#228B22'] # Light to dark green
gex_by_strike = df.groupby('strike_price')['gex'].sum().reset_index() put_colors = ['#FFB6C1', '#DC143C', '#8B0000'] # Light to dark red
# Filter out strikes with no significant exposure # Get unique strike prices and expiration dates
significant_exposure = abs(dex_by_strike['dex']) > abs(dex_by_strike['dex']).max() * 0.01 strike_prices = sorted(df['strike_price'].unique())
min_strike = dex_by_strike[significant_exposure]['strike_price'].min() expiry_dates = sorted(df['date_expiration'].unique())
max_strike = dex_by_strike[significant_exposure]['strike_price'].max()
# Add some padding to the range # Create y-axis ticks for strike prices
strike_padding = (max_strike - min_strike) * 0.1 y_ticks = np.arange(len(strike_prices))
y_min = max(min_strike - strike_padding, dex_by_strike['strike_price'].min())
y_max = min(max_strike + strike_padding, dex_by_strike['strike_price'].max())
# Plot DEX bars # Calculate total exposure for each strike and type
positive_dex = dex_by_strike[dex_by_strike['dex'] > 0] total_exposure = pd.DataFrame()
negative_dex = dex_by_strike[dex_by_strike['dex'] <= 0]
ax.barh(positive_dex['strike_price'], positive_dex['dex'], for expiry in expiry_dates:
color='green', alpha=0.7, label='Positive DEX') expiry_data = df[df['date_expiration'] == expiry]
ax.barh(negative_dex['strike_price'], negative_dex['dex'],
color='#964B00', alpha=0.7, label='Negative DEX') # Process calls
calls = expiry_data[expiry_data['put_call'] == 'CALL']
call_exposure = calls.groupby('strike_price')['delta_exposure'].sum()
# Process puts
puts = expiry_data[expiry_data['put_call'] == 'PUT']
put_exposure = puts.groupby('strike_price')['delta_exposure'].sum()
# Plot calls (positive x-axis)
if not call_exposure.empty:
ax.barh(y_ticks, call_exposure.reindex(strike_prices).fillna(0),
alpha=0.7, left=total_exposure.get('calls', 0),
color=call_colors[expiry_dates.tolist().index(expiry) % len(call_colors)],
height=0.8)
# Plot puts (negative x-axis)
if not put_exposure.empty:
ax.barh(y_ticks, put_exposure.reindex(strike_prices).fillna(0),
alpha=0.7, left=total_exposure.get('puts', 0),
color=put_colors[expiry_dates.tolist().index(expiry) % len(put_colors)],
height=0.8)
# Update total exposure
total_exposure['calls'] = total_exposure.get('calls', 0) + call_exposure.reindex(strike_prices).fillna(0)
total_exposure['puts'] = total_exposure.get('puts', 0) + put_exposure.reindex(strike_prices).fillna(0)
# Plot GEX profile # Add strike price labels
ax.plot(gex_by_strike['gex'], gex_by_strike['strike_price'], ax.set_yticks(y_ticks)
color='yellow', label='GEX Profile', linewidth=2) ax.set_yticklabels([f'${price:.2f}' for price in strike_prices])
# Calculate and plot support/resistance levels # Add current price line
significant_gex = gex_by_strike[abs(gex_by_strike['gex']) > abs(gex_by_strike['gex']).mean()] current_price_idx = np.searchsorted(strike_prices, current_price)
resistance_level = significant_gex[significant_gex['strike_price'] > current_price]['strike_price'].min() ax.axhline(y=current_price_idx, color='red', linestyle='--', alpha=0.5,
support_level = significant_gex[significant_gex['strike_price'] < current_price]['strike_price'].max() label=f'Current Price: ${current_price:.2f}')
# Add reference lines # Format x-axis
if pd.notna(resistance_level): ax.xaxis.set_major_formatter(plt.FuncFormatter(
ax.axhline(y=resistance_level, color='red', linestyle='--', alpha=0.5, lambda x, p: f'${abs(x/1e6):.1f}M' if abs(x) >= 1e6 else f'${abs(x/1e3):.1f}K'))
label=f'Call Resistance: {resistance_level:.1f}')
if pd.notna(support_level):
ax.axhline(y=support_level, color='green', linestyle='--', alpha=0.5,
label=f'Put Support: {support_level:.1f}')
ax.axhline(y=current_price, color='white', linestyle='--', alpha=0.5,
label=f'Spot Price: {current_price:.1f}')
# Calculate and plot HVL # Add labels and title
hvl = dex_by_strike['strike_price'][abs(dex_by_strike['dex']).idxmin()] ax.set_title(f'Delta Hedging Exposure by Strike and Expiration\n{df["ticker"].iloc[0]}',
ax.axhline(y=hvl, color='gray', linestyle='--', alpha=0.5, pad=20)
label=f'HVL: {hvl:.1f}') ax.set_xlabel('Delta Exposure ($)')
ax.set_ylabel('Strike Price ($)')
# Set y-axis limits to focus on relevant region # Add grid
ax.set_ylim(y_min, y_max)
# Customize the plot
ax.set_title(f'Net DEX All Expirations for {df["ticker"].iloc[0]}\nTimestamp: {df["date"].iloc[0]}', pad=20)
ax.set_xlabel('Exposure (Contract-Adjusted)')
ax.set_ylabel('Strike Price')
ax.grid(True, alpha=0.2) ax.grid(True, alpha=0.2)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
# Format axis # Add legend for expiration dates
ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'{x/1e6:.1f}B' if abs(x) >= 1e6 else f'{x/1e3:.1f}M')) legend_elements = []
for i, expiry in enumerate(expiry_dates):
days_to_expiry = (expiry - df['date'].iloc[0]).days
legend_elements.append(plt.Rectangle((0,0), 1, 1,
fc=call_colors[i % len(call_colors)],
alpha=0.7,
label=f'Exp: {expiry.strftime("%Y-%m-%d")} ({days_to_expiry}d)'))
ax.legend(handles=legend_elements, loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout() plt.tight_layout()
return fig return fig
# Main execution
if __name__ == "__main__":
# Assuming we have the data loaded in ticker_data
df = pd.DataFrame(ticker_data)
# Use the functions with your data if not df.empty:
df = pd.DataFrame(ticker_data) # Process the data
processed_df = process_options_data(df) processed_df = process_options_data_by_expiry(df)
current_price = float(df['underlying_price'].iloc[0]) current_price = float(df['underlying_price'].iloc[0])
fig = plot_option_exposure(processed_df, current_price)
plt.show() # Create and show the plot
fig = plot_delta_exposure_by_expiry(processed_df, current_price)
# Print analysis plt.show()
print("\nKey Levels Analysis:")
dex_by_strike = processed_df.groupby('strike_price')['dex'].sum() # Print summary statistics
gex_by_strike = processed_df.groupby('strike_price')['gex'].sum() print("\nDelta Exposure Summary:")
total_call_exposure = processed_df[processed_df['put_call'] == 'CALL']['delta_exposure'].sum()
print(f"Largest DEX Positive: Strike ${dex_by_strike.idxmax():.2f} (${dex_by_strike.max()/1e6:.2f}M)") total_put_exposure = processed_df[processed_df['put_call'] == 'PUT']['delta_exposure'].sum()
print(f"Largest DEX Negative: Strike ${dex_by_strike.idxmin():.2f} (${dex_by_strike.min()/1e6:.2f}M)") net_exposure = total_call_exposure + total_put_exposure
print(f"Largest GEX: Strike ${gex_by_strike.idxmax():.2f} (${gex_by_strike.max()/1e6:.2f}M)")
print(f"Net DEX: ${dex_by_strike.sum()/1e6:.2f}M") print(f"Total Call Delta Exposure: ${total_call_exposure/1e6:.2f}M")
print(f"Net GEX: ${gex_by_strike.sum()/1e6:.2f}M") print(f"Total Put Delta Exposure: ${total_put_exposure/1e6:.2f}M")
print(f"Net Delta Exposure: ${net_exposure/1e6:.2f}M")
else:
print("No data available for analysis")