diff --git a/exchanges/constants/urls.py b/exchanges/constants/urls.py index 859f756..f0627c6 100644 --- a/exchanges/constants/urls.py +++ b/exchanges/constants/urls.py @@ -1,5 +1,5 @@ BINANCE_API_OPTIONS_URL = "https://eapi.binance.com" -BINANCE_API_FUTURES_URL = "https://fapi.binance.com/fapi/v1/premiumIndex" +BINANCE_API_FUTURES_URL = "https://fapi.binance.com" BINANCE_API_SPOT_URL = "https://api.binance.com" ByBit_API_URL = "https://api.bybit.com/v2/public/symbols" OKX_API_URL = "https://www.okex.com/api/spot/v3/instruments" diff --git a/exchanges/fetchers/binance_fetcher.py b/exchanges/fetchers/binance_fetcher.py index 90a3b1d..e9244bd 100644 --- a/exchanges/fetchers/binance_fetcher.py +++ b/exchanges/fetchers/binance_fetcher.py @@ -44,32 +44,86 @@ def fetch_options_symbols(): @staticmethod def fetch_mark_and_underlying_price(): - mark_prices = BinanceFetcher.get_response( + mark_prices_options = BinanceFetcher.get_response( BINANCE_API_OPTIONS_URL + "/eapi/v1/mark" ) underlying_price = BinanceFetcher.get_response( BINANCE_API_SPOT_URL + "/api/v3/ticker/price" ) underlying_price_df = pd.DataFrame(underlying_price) - data_df = pd.DataFrame(mark_prices) - data_df = data_df.loc[data_df["symbol"].str.contains("BTC-")] + mark_prices_options_df = pd.DataFrame(mark_prices_options) + mark_prices_options_df = mark_prices_options_df.loc[ + mark_prices_options_df["symbol"].str.contains("BTC-") + ] + forward_prices = BinanceFetcher.fetch_mark_price_futures() # Ensure that only the BTCUSDT price is fetched to match "BTC-" symbols ud_price = underlying_price_df.loc[ underlying_price_df["symbol"] == "BTCUSDT", "price" ].iloc[0] - data_df["underlying_price"] = float(ud_price) - data_df.rename(columns={"markPrice": "mark_price"}, inplace=True) + mark_prices_options_df["underlying_price"] = float(ud_price) + mark_prices_options_df.rename(columns={"markPrice": "mark_price"}, inplace=True) # Convert "mark_price" to float - data_df["mark_price"] = data_df["mark_price"].astype(float) + mark_prices_options_df["mark_price"] = mark_prices_options_df[ + "mark_price" + ].astype(float) + mark_prices_options_df["expiry"] = ( + mark_prices_options_df["symbol"].str.split("-").str[1] + ) + mark_prices_options_df = mark_prices_options_df.merge( + forward_prices, on="expiry", how="right" + ) + # rename symbol_x to symbol + mark_prices_options_df.rename(columns={"symbol_x": "symbol"}, inplace=True) + print(mark_prices_options_df.head()) - return data_df[["symbol", "mark_price", "underlying_price"]] + return mark_prices_options_df[ + ["symbol", "mark_price", "underlying_price", "forward_price"] + ] + + @staticmethod + def fetch_mark_price_futures(): + symbols = BinanceFetcher.fetch_futures_symbols() + mark_prices = [] # This will hold dictionaries + for symbol in symbols: + data = BinanceFetcher.get_response( + BINANCE_API_FUTURES_URL + f"/fapi/v1/depth?symbol={symbol}" + ) + + bids_df = pd.DataFrame(data["bids"], columns=["price", "quantity"]).astype( + {"price": "float"} + ) + asks_df = pd.DataFrame(data["asks"], columns=["price", "quantity"]).astype( + {"price": "float"} + ) + + # Get maximum bid and minimum ask + best_bid = bids_df["price"].max() + best_ask = asks_df["price"].min() + + forward_price = (best_bid + best_ask) / 2 + expiry = symbol.split("_")[1] + + mark_prices.append( + { + "symbol": symbol, + "best_bid": best_bid, + "best_ask": best_ask, + "forward_price": forward_price, + "expiry": expiry, + } + ) + + mark_prices_df = pd.DataFrame(mark_prices) + return mark_prices_df @staticmethod def fetch_futures_symbols(): - data = BinanceFetcher.get_response(BINANCE_API_FUTURES_URL) + data = BinanceFetcher.get_response( + BINANCE_API_FUTURES_URL + "/fapi/v1/premiumIndex" + ) if data: return [ res.get("symbol") for res in data if "BTCUSDT_" in res.get("symbol", "") diff --git a/exchanges/fetchers/option_fetcher.py b/exchanges/fetchers/option_fetcher.py index 54a57a9..44b7d24 100644 --- a/exchanges/fetchers/option_fetcher.py +++ b/exchanges/fetchers/option_fetcher.py @@ -157,6 +157,7 @@ def process_okx_data(self, df: pd.DataFrame) -> pd.DataFrame: def process_binance_data(self, df: pd.DataFrame) -> pd.DataFrame: prices = self.binance_fetcher.fetch_mark_and_underlying_price() + print(prices) prices["symbol"] = prices["symbol"].apply(self.transform_symbol_format) df["symbol"] = df["symbol"].apply(self.convert_usdt_to_usd) @@ -174,6 +175,7 @@ def process_binance_data(self, df: pd.DataFrame) -> pd.DataFrame: ) df["YTM"] = (df["expiry"] - df["datetime"]) / np.timedelta64(1, "Y") # Merge the prices into the df based on the 'symbol' + print(prices) df = df.merge(prices, on="symbol", how="left") df["bid_spread"] = np.maximum(df["mark_price"] - df["bid"], 0) @@ -186,6 +188,7 @@ def process_binance_data(self, df: pd.DataFrame) -> pd.DataFrame: "ask", "mark_price", "underlying_price", + "forward_price", "bid_spread", "ask_spread", "datetime", diff --git a/exchanges/handlers/merge.py b/exchanges/handlers/merge.py index db1a171..bcab1c3 100644 --- a/exchanges/handlers/merge.py +++ b/exchanges/handlers/merge.py @@ -20,6 +20,9 @@ def handle( self, options_market: List[str], future_market: List[str] | None ) -> pd.DataFrame: options_data = self.option_market_handler.handle(options_market) + options_data.to_json( + f"{self.exchange}_options_data.json", orient="records", indent=4 + ) if future_market: futures_data = self.future_market_handler.handle(future_market) merged_data = pd.concat([options_data, futures_data], ignore_index=True) diff --git a/exchanges/main.py b/exchanges/main.py index 661266e..556649b 100644 --- a/exchanges/main.py +++ b/exchanges/main.py @@ -14,22 +14,22 @@ def main(): try: - # deribit = DeribitManager(pairs_to_load=["BTC/USD:BTC"], market_types=["option"]) - # binance = BinanceManager( - # pairs_to_load=["BTC/USD:BTC"], market_types=["option", "future"] - # ) - # okx = OKXManager(pairs_to_load=["BTC/USD:BTC"], market_types=["option"]) - # global_orderbook = pd.DataFrame() - # - # for manager in [binance, deribit, okx]: - # global_orderbook = pd.concat( - # [global_orderbook, manager.load_specific_pairs()], ignore_index=True - # ) - # global_orderbook.to_json("global_orderbook.json", orient="records", indent=4) - global_orderbook = pd.read_json("global_orderbook.json") - process = Processing() - x = process.process_global_orderbook(global_orderbook) - x.to_json("global_orderbook_processed.json", orient="records", indent=4) + deribit = DeribitManager(pairs_to_load=["BTC/USD:BTC"], market_types=["option"]) + binance = BinanceManager( + pairs_to_load=["BTC/USD:BTC"], market_types=["option", "future"] + ) + okx = OKXManager(pairs_to_load=["BTC/USD:BTC"], market_types=["option"]) + global_orderbook = pd.DataFrame() + + for manager in [binance]: + global_orderbook = pd.concat( + [global_orderbook, manager.load_specific_pairs()], ignore_index=True + ) + global_orderbook.to_json("global_orderbook.json", orient="records", indent=4) + # global_orderbook = pd.read_json("global_orderbook.json") + # process = Processing() + # x = process.process_global_orderbook(global_orderbook) + # x.to_json("global_orderbook_processed.json", orient="records", indent=4) except Exception as e: logger.error(f"An unexpected error occurred in the main function: {e}") diff --git a/exchanges/processing.py b/exchanges/processing.py index 8f41470..f41ebd1 100644 --- a/exchanges/processing.py +++ b/exchanges/processing.py @@ -12,9 +12,9 @@ def calculate_implied_interest_rates(df): # Calculate implied interest rates df = df.copy() - df["rimp"] = (np.log(df["mark_price"]) - np.log(df["underlying_price"])) / df[ - "YTM" - ] + df["rimp"] = ( + np.log(df["forward_price"]) - np.log(df["underlying_price"]) + ) / df["YTM"] # Rimp = (ln F − ln S)/T return df