diff --git a/Optiver/assignment/Basic Quoter.ipynb b/Optiver/assignment/Basic Quoter.ipynb index 0ba3a18..c2d0550 100644 --- a/Optiver/assignment/Basic Quoter.ipynb +++ b/Optiver/assignment/Basic Quoter.ipynb @@ -19,13 +19,32 @@ "import time\n", "import random\n", "import logging\n", + "import statistics # Für rollierende Standardabweichung\n", "\n", "from optibook.synchronous_client import Exchange\n", "from libs import print_positions_and_pnl, round_down_to_tick, round_up_to_tick\n", "\n", "from IPython.display import clear_output\n", "\n", - "logging.getLogger('client').setLevel('ERROR')" + "logging.getLogger('client').setLevel('ERROR')\n", + "\n", + "# === Parameter für Dynamic Spread ===\n", + "ROLLING_WINDOW = 20 # Anzahl der Mid-Prices, über die Volatilität berechnet wird\n", + "VOL_MULTIPLIER = 1.5 # Faktor, um StdDev in Credit-Spreads zu übertragen\n", + "DEFAULT_VOL = 0.01 # Fallback-Volatilität, falls noch nicht genug Daten\n", + "\n", + "# === Statische Konstanten (bleiben erhalten) ===\n", + "QUOTED_VOLUME = 10\n", + "PRICE_RETREAT_PER_LOT = 0.005\n", + "POSITION_LIMIT = 100\n", + "\n", + "# === Connect to Exchange und Instruments holen ===\n", + "exchange = Exchange()\n", + "exchange.connect()\n", + "INSTRUMENTS = exchange.get_instruments()\n", + "\n", + "# === Rolling‐Mid‐Historie initialisieren ===\n", + "mid_history = [] # Liste der letzten Mid‐Preise" ] }, { @@ -123,11 +142,6 @@ "\n", "INSTRUMENTS = exchange.get_instruments()\n", "\n", - "QUOTED_VOLUME = 10\n", - "FIXED_MINIMUM_CREDIT = 0.15\n", - "PRICE_RETREAT_PER_LOT = 0.005\n", - "POSITION_LIMIT = 100\n", - "\n", "while True:\n", " print(f'')\n", " print(f'-----------------------------------------------------------------')\n", @@ -143,8 +157,7 @@ " # Remove all existing (still) outstanding limit orders\n", " exchange.delete_orders(instrument.instrument_id)\n", " \n", - " # Obtain order book and only skip this instrument if there are no bids or offers available at all on that instrument,\n", - " # as we want to decide zwischen einfachem Mid oder VWAP-basiertem Mid\n", + " # Obtain order book; skip if incomplete\n", " instrument_order_book = exchange.get_last_price_book(instrument.instrument_id)\n", " if not (instrument_order_book and instrument_order_book.bids and instrument_order_book.asks):\n", " print(f'{instrument.instrument_id:>6s} -- INCOMPLETE ORDER BOOK')\n", @@ -156,36 +169,56 @@ " # Obtain best bid and ask prices from order book (für Anzeige)\n", " best_bid_price = instrument_order_book.bids[0].price\n", " best_ask_price = instrument_order_book.asks[0].price\n", + " spread_ticks = round((best_ask_price - best_bid_price) / instrument.tick_size)\n", "\n", - " # Berechne Volumina der Top-3-Level je Seite\n", - " bid_vol_top3 = sum(level.volume for level in instrument_order_book.bids[:3])\n", - " ask_vol_top3 = sum(level.volume for level in instrument_order_book.asks[:3])\n", - " use_simple_mid = False\n", + " # --- 1) Versuch Microprice Top-1, falls Spread=1 Tick und ausreichend Volumen ---\n", + " bid_size0 = instrument_order_book.bids[0].volume\n", + " ask_size0 = instrument_order_book.asks[0].volume\n", "\n", - " if bid_vol_top3 + ask_vol_top3 > 0:\n", - " rel_diff = abs(bid_vol_top3 - ask_vol_top3) / max(bid_vol_top3, ask_vol_top3)\n", - " # Wenn sich die Top-Volumina um weniger als 10% unterscheiden, einfachen Mid verwenden\n", - " if rel_diff < 0.10:\n", - " use_simple_mid = True\n", + " if spread_ticks == 1 and (bid_size0 + ask_size0) > 0:\n", + " mid_price = (best_ask_price * bid_size0 + best_bid_price * ask_size0) / (bid_size0 + ask_size0)\n", + " else:\n", + " mid_price = None\n", "\n", - " if use_simple_mid:\n", - " mid_price = (best_bid_price + best_ask_price) / 2.0\n", + " # --- 2) Falls Microprice nicht verwendet wurde, prüfe Top-3 VWAP vs. einfacher Mid ---\n", + " if mid_price is None:\n", + " bid_vol_top3 = sum(level.volume for level in instrument_order_book.bids[:3])\n", + " ask_vol_top3 = sum(level.volume for level in instrument_order_book.asks[:3])\n", + " total_vol3 = bid_vol_top3 + ask_vol_top3\n", + "\n", + " if total_vol3 == 0:\n", + " mid_price = (best_bid_price + best_ask_price) / 2.0\n", + " else:\n", + " rel_diff3 = abs(bid_vol_top3 - ask_vol_top3) / max(bid_vol_top3, ask_vol_top3)\n", + " if rel_diff3 < 0.10:\n", + " mid_price = (best_bid_price + best_ask_price) / 2.0\n", + " else:\n", + " mid_price = vwap_mid_from_top_levels(\n", + " instrument_order_book.bids,\n", + " instrument_order_book.asks,\n", + " depth=3\n", + " )\n", + " if mid_price is None:\n", + " mid_price = (best_bid_price + best_ask_price) / 2.0\n", + "\n", + " # --- 3) Volatilitäts‐basierter dynamic_credit (Spread) ---\n", + " mid_history.append(mid_price)\n", + " if len(mid_history) > ROLLING_WINDOW:\n", + " mid_history.pop(0)\n", + "\n", + " if len(mid_history) >= ROLLING_WINDOW:\n", + " rolling_std = statistics.pstdev(mid_history[-ROLLING_WINDOW:])\n", " else:\n", - " mid_price = vwap_mid_from_top_levels(\n", - " instrument_order_book.bids,\n", - " instrument_order_book.asks,\n", - " depth=3\n", - " )\n", - " if mid_price is None:\n", - " print(f'{instrument.instrument_id:>6s} -- VWAP MID PRICE NOT COMPUTABLE')\n", - " continue\n", + " rolling_std = DEFAULT_VOL\n", + "\n", + " dynamic_credit = VOL_MULTIPLIER * rolling_std\n", "\n", " # Calculate our fair/theoretical price based on the chosen mid_price and our current position\n", " theoretical_price = mid_price - PRICE_RETREAT_PER_LOT * position\n", "\n", - " # Calculate final bid and ask prices to insert\n", - " bid_price = round_down_to_tick(theoretical_price - FIXED_MINIMUM_CREDIT, instrument.tick_size)\n", - " ask_price = round_up_to_tick(theoretical_price + FIXED_MINIMUM_CREDIT, instrument.tick_size)\n", + " # Calculate final bid and ask prices to insert using dynamic_credit\n", + " bid_price = round_down_to_tick(theoretical_price - dynamic_credit, instrument.tick_size)\n", + " ask_price = round_up_to_tick(theoretical_price + dynamic_credit, instrument.tick_size)\n", " \n", " # Calculate bid and ask volumes to insert, taking into account the exchange position_limit\n", " max_volume_to_buy = POSITION_LIMIT - position\n",