change quarter date for hedge funds
This commit is contained in:
parent
5985c54c77
commit
b406a53f68
@ -62,7 +62,8 @@ crypto_con.close()
|
||||
|
||||
load_dotenv()
|
||||
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"):
|
||||
|
||||
@ -27,7 +27,7 @@ warnings.filterwarnings("ignore", category=RuntimeWarning, message="invalid valu
|
||||
start_date = datetime(2015, 1, 1).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"):
|
||||
|
||||
@ -3937,7 +3937,7 @@ async def get_next_earnings(data:TickerData, api_key: str = Security(get_api_key
|
||||
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
|
||||
|
||||
@ -3955,7 +3955,7 @@ async def get_surprise_earnings(data:TickerData, api_key: str = Security(get_api
|
||||
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
|
||||
|
||||
|
||||
219
app/support.py
219
app/support.py
@ -2,6 +2,7 @@ import pandas as pd
|
||||
import numpy as np
|
||||
import ujson
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
from scipy.stats import norm
|
||||
from datetime import datetime, date, timedelta
|
||||
from benzinga import financial_data
|
||||
@ -43,7 +44,7 @@ query_template = """
|
||||
"""
|
||||
|
||||
|
||||
ticker = 'SPY'
|
||||
ticker = 'NVDA'
|
||||
end_date = date.today()
|
||||
start_date = end_date - timedelta(1)
|
||||
end_date_str = end_date.strftime('%Y-%m-%d')
|
||||
@ -57,9 +58,6 @@ volatility = calculate_volatility(df_price)
|
||||
print(start_date, end_date)
|
||||
print('volatility', volatility)
|
||||
|
||||
stock_con.close()
|
||||
etf_con.close()
|
||||
|
||||
def get_data(ticker):
|
||||
res_list = []
|
||||
page = 0
|
||||
@ -89,32 +87,34 @@ print(len(ticker_data))
|
||||
|
||||
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
|
||||
K: Strike price
|
||||
T: Time to expiration (in years)
|
||||
r: Risk-free rate
|
||||
sigma: Volatility
|
||||
"""
|
||||
if T <= 0:
|
||||
if T <= 0 or sigma <= 0 or S <= 0:
|
||||
return 0, 0
|
||||
|
||||
d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
|
||||
#d2 = d1 - sigma * np.sqrt(T)
|
||||
try:
|
||||
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
|
||||
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
|
||||
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
|
||||
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['date_expiration'] = pd.to_datetime(df['date_expiration'])
|
||||
df['date'] = pd.to_datetime(df['date'])
|
||||
df['price'] = pd.to_numeric(df['price'])
|
||||
|
||||
# Calculate time to expiration in years
|
||||
df['T'] = (df['date_expiration'] - df['date']).dt.days / 365.0
|
||||
|
||||
# Parameters for calculations
|
||||
risk_free_rate = 0.05 # Current approximate risk-free rate
|
||||
# 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
|
||||
# Use current risk-free rate
|
||||
risk_free_rate = 0.0525
|
||||
|
||||
# Calculate Greeks for each option
|
||||
greeks = df.apply(lambda row: calculate_option_greeks(
|
||||
@ -138,114 +137,130 @@ def process_options_data(df):
|
||||
row['strike_price'],
|
||||
row['T'],
|
||||
risk_free_rate,
|
||||
sigma,
|
||||
volatility,
|
||||
row['put_call']
|
||||
), axis=1)
|
||||
|
||||
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
|
||||
df['gex'] = df['gamma'] * df['volume'] * contract_multiplier #df['gamma'] * df['open_interest'] * df['volume'] * df['underlying_price']
|
||||
df['dex'] = df['delta'] * df['volume'] * contract_multiplier * df['underlying_price'] * 0.01 #df['delta'] * df['volume'] * df['underlying_price'] *0.01
|
||||
contract_multiplier = 100
|
||||
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
|
||||
|
||||
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')
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
fig, ax = plt.subplots(figsize=(15, 12))
|
||||
|
||||
# Aggregate exposures by strike price
|
||||
dex_by_strike = df.groupby('strike_price')['dex'].sum().reset_index()
|
||||
gex_by_strike = df.groupby('strike_price')['gex'].sum().reset_index()
|
||||
# Create custom colormap for calls and puts
|
||||
call_colors = ['#90EE90', '#32CD32', '#228B22'] # Light to dark green
|
||||
put_colors = ['#FFB6C1', '#DC143C', '#8B0000'] # Light to dark red
|
||||
|
||||
# Filter out strikes with no significant exposure
|
||||
significant_exposure = abs(dex_by_strike['dex']) > abs(dex_by_strike['dex']).max() * 0.01
|
||||
min_strike = dex_by_strike[significant_exposure]['strike_price'].min()
|
||||
max_strike = dex_by_strike[significant_exposure]['strike_price'].max()
|
||||
# Get unique strike prices and expiration dates
|
||||
strike_prices = sorted(df['strike_price'].unique())
|
||||
expiry_dates = sorted(df['date_expiration'].unique())
|
||||
|
||||
# Add some padding to the range
|
||||
strike_padding = (max_strike - min_strike) * 0.1
|
||||
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())
|
||||
# Create y-axis ticks for strike prices
|
||||
y_ticks = np.arange(len(strike_prices))
|
||||
|
||||
# Plot DEX bars
|
||||
positive_dex = dex_by_strike[dex_by_strike['dex'] > 0]
|
||||
negative_dex = dex_by_strike[dex_by_strike['dex'] <= 0]
|
||||
# Calculate total exposure for each strike and type
|
||||
total_exposure = pd.DataFrame()
|
||||
|
||||
ax.barh(positive_dex['strike_price'], positive_dex['dex'],
|
||||
color='green', alpha=0.7, label='Positive DEX')
|
||||
ax.barh(negative_dex['strike_price'], negative_dex['dex'],
|
||||
color='#964B00', alpha=0.7, label='Negative DEX')
|
||||
for expiry in expiry_dates:
|
||||
expiry_data = df[df['date_expiration'] == expiry]
|
||||
|
||||
# Plot GEX profile
|
||||
ax.plot(gex_by_strike['gex'], gex_by_strike['strike_price'],
|
||||
color='yellow', label='GEX Profile', linewidth=2)
|
||||
# Process calls
|
||||
calls = expiry_data[expiry_data['put_call'] == 'CALL']
|
||||
call_exposure = calls.groupby('strike_price')['delta_exposure'].sum()
|
||||
|
||||
# Calculate and plot support/resistance levels
|
||||
significant_gex = gex_by_strike[abs(gex_by_strike['gex']) > abs(gex_by_strike['gex']).mean()]
|
||||
resistance_level = significant_gex[significant_gex['strike_price'] > current_price]['strike_price'].min()
|
||||
support_level = significant_gex[significant_gex['strike_price'] < current_price]['strike_price'].max()
|
||||
# Process puts
|
||||
puts = expiry_data[expiry_data['put_call'] == 'PUT']
|
||||
put_exposure = puts.groupby('strike_price')['delta_exposure'].sum()
|
||||
|
||||
# Add reference lines
|
||||
if pd.notna(resistance_level):
|
||||
ax.axhline(y=resistance_level, color='red', linestyle='--', alpha=0.5,
|
||||
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}')
|
||||
# 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)
|
||||
|
||||
# Calculate and plot HVL
|
||||
hvl = dex_by_strike['strike_price'][abs(dex_by_strike['dex']).idxmin()]
|
||||
ax.axhline(y=hvl, color='gray', linestyle='--', alpha=0.5,
|
||||
label=f'HVL: {hvl:.1f}')
|
||||
# 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)
|
||||
|
||||
# Set y-axis limits to focus on relevant region
|
||||
ax.set_ylim(y_min, y_max)
|
||||
# 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)
|
||||
|
||||
# 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')
|
||||
# Add strike price labels
|
||||
ax.set_yticks(y_ticks)
|
||||
ax.set_yticklabels([f'${price:.2f}' for price in strike_prices])
|
||||
|
||||
# Add current price line
|
||||
current_price_idx = np.searchsorted(strike_prices, current_price)
|
||||
ax.axhline(y=current_price_idx, color='red', linestyle='--', alpha=0.5,
|
||||
label=f'Current Price: ${current_price:.2f}')
|
||||
|
||||
# Format x-axis
|
||||
ax.xaxis.set_major_formatter(plt.FuncFormatter(
|
||||
lambda x, p: f'${abs(x/1e6):.1f}M' if abs(x) >= 1e6 else f'${abs(x/1e3):.1f}K'))
|
||||
|
||||
# Add labels and title
|
||||
ax.set_title(f'Delta Hedging Exposure by Strike and Expiration\n{df["ticker"].iloc[0]}',
|
||||
pad=20)
|
||||
ax.set_xlabel('Delta Exposure ($)')
|
||||
ax.set_ylabel('Strike Price ($)')
|
||||
|
||||
# Add grid
|
||||
ax.grid(True, alpha=0.2)
|
||||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
|
||||
|
||||
# Format axis
|
||||
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'))
|
||||
# Add legend for expiration dates
|
||||
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()
|
||||
return fig
|
||||
|
||||
# Use the functions with your data
|
||||
df = pd.DataFrame(ticker_data)
|
||||
processed_df = process_options_data(df)
|
||||
current_price = float(df['underlying_price'].iloc[0])
|
||||
fig = plot_option_exposure(processed_df, current_price)
|
||||
plt.show()
|
||||
# Main execution
|
||||
if __name__ == "__main__":
|
||||
# Assuming we have the data loaded in ticker_data
|
||||
df = pd.DataFrame(ticker_data)
|
||||
|
||||
# Print analysis
|
||||
print("\nKey Levels Analysis:")
|
||||
dex_by_strike = processed_df.groupby('strike_price')['dex'].sum()
|
||||
gex_by_strike = processed_df.groupby('strike_price')['gex'].sum()
|
||||
if not df.empty:
|
||||
# Process the data
|
||||
processed_df = process_options_data_by_expiry(df)
|
||||
current_price = float(df['underlying_price'].iloc[0])
|
||||
|
||||
print(f"Largest DEX Positive: Strike ${dex_by_strike.idxmax():.2f} (${dex_by_strike.max()/1e6:.2f}M)")
|
||||
print(f"Largest DEX Negative: Strike ${dex_by_strike.idxmin():.2f} (${dex_by_strike.min()/1e6:.2f}M)")
|
||||
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"Net GEX: ${gex_by_strike.sum()/1e6:.2f}M")
|
||||
# Create and show the plot
|
||||
fig = plot_delta_exposure_by_expiry(processed_df, current_price)
|
||||
plt.show()
|
||||
|
||||
# Print summary statistics
|
||||
print("\nDelta Exposure Summary:")
|
||||
total_call_exposure = processed_df[processed_df['put_call'] == 'CALL']['delta_exposure'].sum()
|
||||
total_put_exposure = processed_df[processed_df['put_call'] == 'PUT']['delta_exposure'].sum()
|
||||
net_exposure = total_call_exposure + total_put_exposure
|
||||
|
||||
print(f"Total Call Delta Exposure: ${total_call_exposure/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")
|
||||
Loading…
x
Reference in New Issue
Block a user