Execution

Feature Engineering for Options Trading: Build Better Data Inputs

·12 min read

Feature engineering transforms raw market data into structured inputs that power algorithmic trading models. For options traders—whether working with NSE index contracts or global equity options—the quality of engineered features directly determines model reliability and strategy profitability. This process sits at the intersection of domain expertise, statistical thinking, and clean data work.

Why Options Data Needs Special Feature Treatment

Options markets generate rich, multi-dimensional data: strike prices, implied volatility, time decay, volume, open interest, and Greeks all interact in non-linear ways. Unlike a simple stock price, an option’s behavior depends on six interconnected inputs—underlying level, strike, expiry, volatility, rates, and dividends. Raw data alone doesn’t capture these relationships; you must engineer features that expose them.

Consider a NIFTY weekly option at 23,200 strike when the index sits at 23,150. The option’s premium moves not just with the index, but with how fast implied volatility is changing, how many days remain, and what price action suggests about future volatility. A model trained on raw closing price alone will miss these drivers. Feature engineering surfaces them.

The stakes are concrete. A poorly engineered feature set can mislead a model into fitting noise—overfitting on patterns that won’t repeat. A well-designed feature set captures true signal and lets a model generalize to unseen market regimes.

Understanding Your Options Data Before You Build

Before engineering a single feature, immerse yourself in the data itself. Print summary statistics, plot distributions, check for gaps and outliers. For options, this means examining:

  • Historical price volatility: how much the underlying moved day-to-day over the past month, quarter, year
  • Trading volumes: how many contracts changed hands at each strike and expiry
  • Open interest patterns: which strikes attract sustained positions; which are dead
  • Time-to-expiry decay: how premiums evaporate as expiry nears
  • Implied volatility levels and term structure: whether front-month options are more or less expensive (relative to realized vol) than back-month ones

This reconnaissance work is unglamorous but essential. You’ll spot data quality issues, understand seasonal patterns, and get intuition for what features might matter.

For NSE options, check whether your data includes expiration adjustments (NSE weeklies expire every Wednesday), whether dividends are baked in, and whether corporate actions (stock splits, bonus issues) have distorted historical series. Global traders working with, say, S&P 500 index options should verify whether your data covers the actual tradeable contracts (SPX, not SPY) and confirm dividend treatment.

Computing Rolling Historical Volatility

Historical volatility—the standard deviation of past returns, annualized—is among the most fundamental features in options work. It estimates realized volatility and serves as a baseline for comparing implied volatility.

The calculation is straightforward:

  1. Compute daily percentage returns from closing prices
  2. Calculate the standard deviation over a rolling window (typically 20–30 days for options)
  3. Annualize by multiplying by the square root of trading days per year (252 in equity markets)

A 30-day rolling window captures one month of realized movement. A 60-day window smooths over longer cycles. The choice depends on your strategy horizon and the regime you’re trying to predict.

Here’s the core logic:

Daily return = (Close today – Close yesterday) / Close yesterday
Rolling std dev (30 days) = std(last 30 daily returns)
Annualized volatility = Rolling std dev × √252

In Python, pandas makes this trivial:

import pandas as pd
import numpy as np

df['daily_return'] = df['close'].pct_change()

# 30-day rolling historical volatility
window = 30
df['hist_vol_30d'] = df['daily_return'].rolling(window=window).std() * np.sqrt(252)

The resulting hist_vol_30d column now captures the volatility regime at each date. You can plot it over time to see regime shifts: quiet periods drop below 15%, volatile corrections spike above 35%. This feature helps your model understand context—is the market calm or stressed?—and informs how it should weigh other signals.

Building Momentum and Trend Features from Price Action

Technical indicators distill price-and-volume history into single numbers that signal momentum, trend, or mean reversion. For options traders, these features often capture directional bias or overbought/oversold conditions that can inform whether to leg into spreads or expect reversions.

Three canonical indicators deserve inclusion:

Moving Averages: A 20-day simple moving average (SMA) smooths recent price action; a 50-day SMA captures intermediate trend. When the 20-day crosses above the 50-day, momentum may be turning positive. This crossover is itself a feature—a binary signal (1 if above, 0 if below) or a distance metric (20-day minus 50-day).

df['sma_20'] = df['close'].rolling(window=20).mean()
df['sma_50'] = df['close'].rolling(window=50).mean()
df['ma_crossover'] = (df['sma_20'] > df['sma_50']).astype(int)
df['ma_distance'] = df['sma_20'] - df['sma_50']

Relative Strength Index (RSI): RSI measures the magnitude of recent gains versus losses, scaled from 0 to 100. RSI above 70 often signals overbought (potential pullback); below 30 signals oversold (potential bounce). For options, an overbought underlying might favor short calls or call spreads; oversold might favor long calls or bull call spreads.

RSI is computed as:

RS = Average gain over N periods / Average loss over N periods
RSI = 100 - (100 / (1 + RS))

A 14-period RSI is standard. The formula can be computed with talib or pandas_ta, but understanding the logic matters: RSI concentrates on relative strength, not absolute price direction.

Bollinger Bands: Upper and lower bands sit two standard deviations above and below a 20-day moving average. When price touches the upper band, it’s stretched; when it touches the lower band, it’s squeezed. The bands also expand in high-volatility regimes and contract in calm ones. A feature could be price’s position within the bands (0 if at lower band, 1 if at upper) or the band width itself (width as a % of the midline captures volatility regime).

Each of these indicators should be computed and stored as separate columns in your DataFrame. Together, they form a feature vector that captures trend, momentum, and overbought/oversold extremes.

Volatility-Specific Features for Options

Implied volatility (IV) is the market’s priced-in expectation of future volatility. Unlike historical volatility, which is realized volatility from the past, IV is forward-looking.

Implied Volatility Rank (IVR) compares today’s IV to its 52-week (or other historical) range. If IV sits at the 90th percentile of its range, options are expensive relative to their recent history—you might prefer selling premium. If IV sits at the 10th percentile, options are cheap—you might prefer buying.

window = 252  # 52 weeks of trading days
df['iv_52w_high'] = df['implied_vol'].rolling(window=window).max()
df['iv_52w_low'] = df['implied_vol'].rolling(window=window).min()
df['iv_rank'] = (df['implied_vol'] - df['iv_52w_low']) / (df['iv_52w_high'] - df['iv_52w_low'])

IV rank ranges from 0 to 1. An IVR of 0.75 means current IV is 75% of the way between its 52-week low and high—expensive.

IV Percentile is similar but uses a longer lookback (sometimes 252-week or lifetime). IV Skew captures the difference in IV between out-of-the-money puts and out-of-the-money calls. In equity markets, skew often rises during downturns (puts become expensive as tail-risk hedging demand increases), so skew itself can be a feature signaling market stress.

For NSE NIFTY options, you might compute IV rank over the past 20 weeks (close to four months of weekly contract rolls), since weekly options reset each Wednesday. This keeps the rank responsive to near-term regime without noise from old contracts.

Time Decay and Extrinsic Value Features

Options lose value as expiry approaches—a phenomenon called theta decay. All else equal, an out-of-the-money option loses premium daily as the probability it finishes in-the-money shrinks. Time decay is non-linear: it accelerates in the final week before expiry.

Engineering time decay as a feature:

df['days_to_expiry'] = (df['expiry_date'] - df['date']).dt.days
df['time_decay_factor'] = 1 / (df['days_to_expiry'] + 1)  # accelerates toward expiry
df['theta_per_day'] = df['option_price'] / (df['days_to_expiry'] + 1)  # rough proxy

A model can use days_to_expiry directly or transform it (logarithm, square root, 1/x) to capture the nonlinearity of theta. The time_decay_factor emphasizes the final days; theta peaks in the last seven days before expiry.

You might also engineer the extrinsic value—the portion of the option’s price above intrinsic value. For a call with intrinsic value of 0 (out-of-the-money), extrinsic value equals the full premium. As expiry nears, extrinsic value decays to zero. A feature capturing extrinsic value per day remaining normalizes this decay and lets the model learn how fast premium erodes in different volatility regimes.

Volume and Liquidity Features

A liquid option (high volume, tight bid-ask spread) is tradeable; an illiquid one is a trap. Even if your signal is perfect, you can’t exit a dead option without slippage.

Features to engineer:

  • Volume moving average: 20-day average daily volume tells you typical liquidity
  • Volume ratio: today’s volume divided by the 20-day average; readings above 1.5 flag unusual activity
  • Open interest trend: is OI rising (positions opening) or falling (positions closing)? Rising OI on a vol move often suggests continuation; falling OI suggests reversal or distribution
  • Bid-ask spread: if you have it, track spread as an absolute amount and as a % of midprice

For NSE NIFTY weeklies, many strikes are liquid (tight spreads); far out-of-the-money strikes may be sparse. A feature flagging low-OI strikes lets your model avoid or underweight dead contracts.

Lagged Returns and Autoregressive Structure

Markets exhibit short-term dependencies: a large up-move yesterday can increase the odds of continued strength today (or exhaust momentum and reverse). Lagged features capture these dependencies.

df['return_lag_1'] = df['daily_return'].shift(1)  # 1 day ago
df['return_lag_5'] = df['daily_return'].shift(5)  # 5 days ago
df['return_lag_22'] = df['daily_return'].shift(22)  # 1 month ago

A model can learn, for example, that large reversals (big up-moves followed by reversals) signal mean reversion, while sustained moves signal momentum. Lagged volatility is also informative:

df['vol_lag_1'] = df['hist_vol_30d'].shift(1)
df['vol_lag_5'] = df['hist_vol_30d'].shift(5)

Volatility clusters: high vol today predicts high vol tomorrow. This persistence is useful for gauging whether the current regime will hold long enough for a trade to work.

Dimensionality and Feature Selection

It’s tempting to engineer hundreds of features—rolling windows of every indicator, lags of everything, combinations of combinations. Resist this urge. Too many features cause overfitting: the model learns noise, not signal.

If you engineer 100 features and have only 500 data points, you’ve created a system that will fit perfectly to your historical data but fail on forward data. This is the curse of dimensionality.

Instead:

  • Start with a small, focused set of features (historical volatility, moving average crossover, RSI, IV rank, days to expiry)
  • Train your model and examine which features the model actually uses (feature importance in tree models, coefficients in linear models)
  • Drop features with near-zero importance
  • Orthogonalize: if two features are highly correlated, drop one; they’re redundant

Correlation analysis is your friend:

import matplotlib.pyplot as plt
corr_matrix = df[feature_columns].corr()
plt.figure(figsize=(10, 8))
plt.imshow(corr_matrix, cmap='coolwarm')
plt.colorbar()
plt.show()

If two features correlate above 0.8, one is likely redundant. Keep the one that makes more intuitive sense or has better predictive power in isolation.

Practical Assembly into a Feature Matrix

Once you’ve engineered your features, assemble them into a clean DataFrame:

feature_cols = ['hist_vol_30d', 'sma_20', 'sma_50', 'ma_crossover', 'rsi_14', 
                'iv_rank', 'days_to_expiry', 'return_lag_1', 'return_lag_5',
                'volume_ratio', 'bid_ask_spread_pct']

df_model = df[feature_cols + ['target']].dropna()  # target = your label (e.g., future return)

Handle missing values explicitly—drop rows where any feature is NaN (conservative) or forward-fill/interpolate (if justified by domain knowledge). For options, missing IV data usually means no trades that day; drop the row rather than guess.

Standardize features before feeding them to most algorithms:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_scaled = scaler.fit_transform(df_model[feature_cols])

Most models (neural networks, SVM, gradient boosting) perform better when features are normalized to mean 0, standard deviation 1. Tree-based models (Random Forest, XGBoost) don’t require scaling, but it doesn’t hurt.

From Features to Edge

Feature engineering is where domain expertise lives. A quant who understands options theory—vega, gamma, theta, the role of volatility mean reversion—will engineer better features than someone blindly applying technical indicators.

Your edge comes from capturing relationships that the market hasn’t fully priced. If you notice that IV spikes cluster after gaps, engineer a feature that flags gap-days and correlate it to subsequent IV behavior. If you observe that BANKNIFTY weeklies have seasonal patterns (certain strikes trade higher on roll days), encode that calendar knowledge into features.

The best feature set for a NIFTY strangle strategy (selling both an out-of-the-money call and put) might differ from the best set for a delta-hedged calendar spread. Tailor your engineering to your intended strategy.

Key takeaways

  • Historical volatility (rolling std dev of returns, annualized) is foundational — it captures realized market movement and provides context for assessing whether implied volatility is cheap or rich.
  • Technical indicators (moving averages, RSI, Bollinger Bands) encode trend and overbought/oversold conditions — useful for timing entries and flagging reversals or continuation.
  • Implied volatility rank compares current IV to its historical range — tells you if options are expensive (high rank) or cheap (low rank) relative to recent history, informing whether to buy or sell premium.
  • Time decay accelerates toward expiry; engineer days-to-expiry and extrinsic-value features — models need to learn how theta erodes premium at different rates depending on moneyness and volatility regime.
  • Volume and open interest reveal liquidity and position flow — avoid illiquid strikes and recognize when unusual volume signals breakout or distribution.
  • Lagged returns and volatility capture short-term dependencies — markets exhibit clustering (high vol today predicts high vol tomorrow) and mean reversion, both exploitable by learned features.
  • More features is not better; correlation and feature importance analysis prevent overfitting — a lean, orthogonal feature set generalizes better than a bloated one fit to historical noise.
  • Domain expertise matters most — the best features arise when you understand options Greeks, volatility behavior, and your target strategy deeply enough to anticipate which relationships the market might misprice.

Further reading

Algorithmic Trading Pro: Options Trading with Python—Learn to Trade Like a Snake

Options trading is inherently risky and involves leverage; this article is educational only and not financial advice. Always backtest features thoroughly on out-of-sample data and validate edge before deploying real capital.

The daily dispatch
One note a morning.

Each day’s reading-room note, the market outlook, and the strategies that gained the most last session — one short email.