SPB Git

spb/wp7_uqo Public

UQO Working Paper No. 7 — Options-implied information for cross-asset return and volatility prediction: evidence from 3.8B option contracts.

Python 66.5% TeX 32.7% Makefile 0.8%
6.4 KB · 152 lines python
Raw Blame History
1# =============================================================================2# Author: Simon-Pierre Boucher3# Contact: contact@spboucher.ai4# =============================================================================5"""Central configuration: paths, ticker universes, feature sets and constants.67Every path is derived from the repository root and can be overridden through8environment variables, so the pipeline runs from any checkout location:910* ``WP7_RAW_DATA_DIR``  — directory holding the raw DuckDB stores11  (``options.duckdb``, ``stock_5min.duckdb``, ``etf_5min.duckdb``,12  ``index_5min.duckdb``). Defaults to ``<repo>/data/raw``.13* ``WP7_RESULTS_DIR``   — output directory for result tables14  (defaults to ``<repo>/results``).15"""1617import os18from pathlib import Path1920# --------------------------------------------------------------------------21# Paths22# --------------------------------------------------------------------------23PROJECT_ROOT = Path(__file__).resolve().parents[2]2425DATA_RAW = Path(os.environ.get("WP7_RAW_DATA_DIR", PROJECT_ROOT / "data" / "raw"))26DATA_PROCESSED = PROJECT_ROOT / "data" / "processed"27RESULTS_DIR = Path(os.environ.get("WP7_RESULTS_DIR", PROJECT_ROOT / "results"))28FIGURES_DIR = PROJECT_ROOT / "figures"2930# Raw DuckDB stores (external — ~3.8B option records, 11.5B intraday bars)31RAW_DB_FILES = {32    "options": "options.duckdb",33    "stocks_5min": "stock_5min.duckdb",34    "etfs_5min": "etf_5min.duckdb",35    "indices_5min": "index_5min.duckdb",36}3738# Processed (derived) datasets39MERGED_PARQUET = DATA_PROCESSED / "merged_options_rv.parquet"40OPTIONS_FEATURES_PARQUET = DATA_PROCESSED / "options_features.parquet"41REALIZED_VOL_PARQUET = DATA_PROCESSED / "realized_vol.parquet"42CORRELATION_DIVERGENCE_PARQUET = DATA_PROCESSED / "correlation_divergence.parquet"43PRICE_MAGNET_PARQUET = DATA_PROCESSED / "price_magnet_data.parquet"444546def ensure_output_dirs() -> None:47    """Create the output directories if they do not exist yet."""48    for d in (DATA_PROCESSED, RESULTS_DIR, FIGURES_DIR):49        d.mkdir(parents=True, exist_ok=True)505152# --------------------------------------------------------------------------53# Ticker universes54# --------------------------------------------------------------------------55MAJOR_TICKERS = [56    'AAPL', 'MSFT', 'AMZN', 'GOOG', 'GOOGL', 'TSLA', 'NVDA', 'META', 'JPM', 'BAC',57    'WFC', 'GS', 'JNJ', 'UNH', 'PFE', 'XOM', 'CVX', 'HD', 'PG', 'KO',58    'DIS', 'NFLX', 'INTC', 'AMD', 'CRM', 'COST', 'ABBV', 'MRK', 'TMO', 'ABT',59    'V', 'MA', 'BRK_B', 'LLY', 'AVGO', 'ADBE', 'CSCO', 'PEP', 'WMT', 'MCD',60    'TXN', 'QCOM', 'LOW', 'UPS', 'CAT', 'GE', 'BA', 'RTX', 'HON', 'LMT',61]6263ETF_TICKERS = [64    'SPY', 'QQQ', 'IWM', 'DIA', 'TLT', 'GLD', 'XLF', 'XLE', 'XLK', 'XLV',65    'XLI', 'XLP', 'XLU', 'XLB', 'XLC', 'XLRE', 'XLY',66]6768INDEX_SYMBOLS = ['SPX', 'NDX', 'RUT', 'VIX', 'DJI']69INDEX_OPTION_TICKERS = ['SPX', 'NDX', 'RUT']7071# Non-stock tickers (used to isolate the individual-stock cross-section)72NON_STOCK_TICKERS = ETF_TICKERS + INDEX_SYMBOLS7374# 30 large S&P 500 constituents used for the implied-correlation index (RQ3)75SPX_CONSTITUENTS = [76    'AAPL', 'MSFT', 'AMZN', 'GOOG', 'GOOGL', 'TSLA', 'NVDA', 'META', 'JPM', 'BAC',77    'WFC', 'GS', 'JNJ', 'UNH', 'PFE', 'XOM', 'CVX', 'HD', 'PG', 'KO',78    'DIS', 'NFLX', 'INTC', 'AMD', 'CRM', 'V', 'MA', 'LLY', 'AVGO', 'ADBE',79]8081# Liquid names used for the Greeks-decay / price-magnet analysis (RQ4)82RQ4_TICKERS = ['AAPL', 'MSFT', 'AMZN', 'GOOG', 'TSLA', 'NVDA', 'META', 'JPM', 'SPY', 'QQQ']8384# --------------------------------------------------------------------------85# Feature sets86# --------------------------------------------------------------------------87# The ten option-implied predictors of the return-predictability regressions88RQ1_FEATURES = [89    'iv_atm_30d', 'iv_term_slope', 'iv_skew_25d', 'implied_skewness',90    'implied_kurtosis_proxy', 'pc_volume_ratio', 'pc_oi_ratio',91    'net_gamma_exposure', 'rv_daily', 'rv_w',92]9394HAR_FEATURES = ['rv_lag1', 'rv_w', 'rv_m']95IV_FEATURES = ['iv_atm_30d', 'iv_term_slope', 'iv_skew_25d',96               'implied_skewness', 'implied_kurtosis_proxy']9798# Competing realized-variance forecasting models (RQ2)99RQ2_MODELS = {100    'HAR-RV': ['rv_lag1', 'rv_w', 'rv_m'],101    'GARCH_proxy': ['rv_lag1', 'sq_return'],102    'IV_only': ['iv_atm_30d', 'iv_atm_90d', 'iv_term_slope', 'iv_skew_25d'],103    'IV_surface': ['iv_atm_30d', 'iv_atm_90d', 'iv_term_slope', 'iv_skew_25d',104                   'implied_skewness', 'implied_kurtosis_proxy'],105    'HAR-RV + IV_surface': ['rv_lag1', 'rv_w', 'rv_m',106                            'iv_atm_30d', 'iv_atm_90d', 'iv_term_slope',107                            'iv_skew_25d', 'implied_skewness', 'implied_kurtosis_proxy'],108}109110# Variables sorted on in the portfolio analysis111SORT_VARIABLES = {112    'iv_atm_30d': 'ATM IV (30d)',113    'iv_skew_25d': 'Volatility Skew (25d)',114    'implied_skewness': 'Implied Skewness',115    'implied_kurtosis_proxy': 'Implied Kurtosis',116    'pc_volume_ratio': 'Put-Call Volume Ratio',117    'pc_oi_ratio': 'Put-Call OI Ratio',118    'iv_term_slope': 'IV Term Structure Slope',119}120121# --------------------------------------------------------------------------122# Sample partitions123# --------------------------------------------------------------------------124SUBPERIODS = {125    'Pre-GFC Recovery (2010-2012)': ('2010-01-01', '2012-12-31'),126    'Bull Market (2013-2016)': ('2013-01-01', '2016-12-31'),127    'Low Vol Era (2017-2018)': ('2017-01-01', '2018-12-31'),128    'Pre-COVID (2019)': ('2019-01-01', '2019-12-31'),129    'COVID Period (2020)': ('2020-01-01', '2020-12-31'),130    'Post-COVID Bull (2021)': ('2021-01-01', '2021-12-31'),131    'Rate Hiking (2022)': ('2022-01-01', '2022-12-31'),132    'Recovery (2023-2024)': ('2023-01-01', '2024-12-31'),133    'Recent (2025)': ('2025-01-01', '2025-12-31'),134}135136CRISES = {137    'Flash Crash (2010-05-06)': '2010-05-06',138    'Euro Crisis (2011-08-05)': '2011-08-05',139    'China Deval (2015-08-24)': '2015-08-24',140    'Volmageddon (2018-02-05)': '2018-02-05',141    'COVID Crash (2020-03-16)': '2020-03-16',142    'Meme Stocks (2021-01-27)': '2021-01-27',143    'Rate Shock (2022-06-13)': '2022-06-13',144    'SVB Crisis (2023-03-10)': '2023-03-10',145    'Aug VIX Spike (2024-08-05)': '2024-08-05',146}147148VIX_REGIME_BINS = [0, 15, 20, 25, 35, 100]149VIX_REGIME_LABELS = ['VeryLow', 'Low', 'Medium', 'High', 'Crisis']150VIX_REGIME_LABELS_VERBOSE = ['Very Low (<15)', 'Low (15-20)', 'Medium (20-25)',151                             'High (25-35)', 'Crisis (>35)']152