Skip to content

Commit

Permalink
feat(exchanges): calculate_forward_and_atm
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakl committed Mar 11, 2024
1 parent 4e83e7f commit ab88755
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
2 changes: 2 additions & 0 deletions exchanges/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def main():
consolidated_options = Processing().consolidate_quotes(global_orderbook_options)

process_quotes = Processing().process_quotes(consolidated_options)
calculate_forward_and_atm = Processing().calculate_forward_and_atm(process_quotes)
calculate_forward_and_atm.to_json("forward_and_atm.json", orient="records", indent=4)
process_quotes.to_json("quotes.json", orient="records", indent=4)

except Exception as e:
Expand Down
50 changes: 42 additions & 8 deletions exchanges/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def eliminate_invalid_quotes(df):

@staticmethod
def process_quotes(df: pd.DataFrame) -> pd.DataFrame:
df = df.copy()
# Calculate spreads
df['bid_spread'] = df['mark_price'] - df['bid']
df['ask_spread'] = df['ask'] - df['mark_price']
Expand All @@ -81,19 +82,52 @@ def process_quotes(df: pd.DataFrame) -> pd.DataFrame:
# Calculate GMS
GMS = SPREAD_MIN * SPREAD_MULTIPLIER

# Filter using corrected conditions
df = df[(df['spread'] <= GMS) | (df['spread'] <= df['MAS'])].index
df = df[(df['spread'] <= GMS) | (df['spread'] <= df['MAS'])]

# Ensure modifications are made on the original DataFrame to avoid the warning
df["mid_price"] = (df["bid"] + df["ask"]) / 2
# Extract strike price and option type (Call/Put) from the symbol
df['strike'] = df['symbol'].apply(lambda x: int(x.split('-')[2]))
df['option_type'] = df['symbol'].apply(lambda x: x[-1])

# Return the filtered DataFrame without temporary columns
df["mid_price"] = (df["bid"] + df["ask"]) / 2
return df

@staticmethod
def calculate_mid_price(df):
df["mid_price"] = (df["bid"] + df["ask"]) / 2
return df
def calculate_forward_and_atm(df):
# YTM index
ytm = 30 / 365

# Merge call and put options by strike
calls = df[df['option_type'] == 'C'].set_index('strike')
puts = df[df['option_type'] == 'P'].set_index('strike')
merged = calls.join(puts, lsuffix='_call', rsuffix='_put', how='inner')

# Calculate the implied forward price for each strike
merged['F_imp'] = merged.index + np.exp(ytm) * (merged['mid_price_call'] - merged['mid_price_put'])

# Find the strike with minimum absolute mid-price difference
merged['mid_price_diff'] = abs(merged['mid_price_call'] - merged['mid_price_put'])
min_diff_strike = merged['mid_price_diff'].idxmin()
F_imp_at_min_diff = merged.loc[min_diff_strike, 'F_imp']
print(F_imp_at_min_diff)
F_imp_at_min_diff.to_json("forward_and_atm.json", orient="records", indent=4)
# Set the largest strike less than F_imp as ATM strike
possible_atm_strikes = [strike for strike in merged.index if strike < F_imp_at_min_diff]
if possible_atm_strikes:
K_ATM = max(possible_atm_strikes)
else:
K_ATM = None

# Select OTM options
otm_options = df[
(df['strike'] > K_ATM) & (df['option_type'] == 'C') | (df['strike'] < K_ATM) & (df['option_type'] == 'P')]

# If both call and put options for the same strike, average them
if K_ATM and K_ATM in calls.index and K_ATM in puts.index:
atm_price = (calls.loc[K_ATM, 'mid_price'] + puts.loc[K_ATM, 'mid_price']) / 2
else:
atm_price = None

return F_imp_at_min_diff, K_ATM, otm_options, atm_price

@staticmethod
def atm_strike(df):
Expand Down

0 comments on commit ab88755

Please sign in to comment.