DEV Community

shakti tiwari
shakti tiwari

Posted on • Originally published at optiontradingwithai.in

AI Options Trading on the Hang Seng Index

AI Options Trading on the Hang Seng Index

Answer-first: Build an AI-assisted options-trading bot for the Hang Seng Index (HSI) by combining a feature pipeline (options chain, implied volatility, PCR), a gradient-boosting classifier for directional probability, and a backtest that enforces Greeks-based risk limits. The model emits a probability; a rules engine decides whether to act. Here is a runnable Python scaffold.

Written for retail quants targeting the Hang Seng Index (HSI) market (HKEX, regulator SFC).

Educational only. Not investment advice. Options can lose their full value. Consult SFC and a licensed advisor.

Why Hang Seng Index (HSI) options are a strong AI target

  • Concentrated liquidity at major strikes -> cleaner labels than broad ETFs.
  • VHSI (Hang Seng Volatility Index) -> a native regime signal.
  • SFC clarity -> transparent cost disclosure.

Architecture (three layers)

1. Data/Feature Pipeline -> chain, IV, PCR
2. Model (gradient boosting) -> P(direction | features)
3. Rules + Greeks Engine -> sizing, stop, DTE limit
Enter fullscreen mode Exit fullscreen mode

Layer 1 — Features (Python)

# Mac / Linux / Termux
python3 features.py
# Windows CMD
py features.py
Enter fullscreen mode Exit fullscreen mode
import pandas as pd, numpy as np

def build_features(chain: pd.DataFrame, pcr: float) -> pd.DataFrame:
    df = chain.copy()
    df["mid"] = (df["bid"] + df["ask"]) / 2.0
    df["spread_pct"] = (df["ask"] - df["bid"]) / df["mid"].clip(lower=1e-9)
    df["moneyness"] = df["strike"] / df["spot"] - 1.0
    atm_iv = df.loc[(df["moneyness"].abs()).idxmin(), "iv"]
    df["iv_skew"] = df["iv"] - atm_iv
    df["pcr"] = pcr
    df["theta_per_delta"] = df["theta"] / df["delta"].clip(lower=1e-9)
    return df

if __name__ == "__main__":
    demo = pd.DataFrame([{"strike": 18000.0, "bid": 18000.0*0.004, "ask": 18000.0*0.0044,
        "iv": 0.18, "delta": 0.5, "gamma": 0.002, "theta": -40.0, "vega": 95.0,
        "oi": 50000, "volume": 3000, "spot": 17920.0, "dte": 15}])
    f = build_features(demo, pcr=0.97)
    print(f[["mid","spread_pct","moneyness","iv_skew","theta_per_delta"]].to_string())
Enter fullscreen mode Exit fullscreen mode

Layer 2 — Model (HistGradientBoosting)

# Mac / Linux / Termux
python3 train.py
# Windows CMD
py train.py
Enter fullscreen mode Exit fullscreen mode
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import TimeSeriesSplit, roc_auc_score
import pandas as pd

FEATURES = ["spread_pct","moneyness","iv_skew","pcr",
            "theta_per_delta","gamma","vega","dte","oi","volume"]

def train(X: pd.DataFrame, y: pd.Series):
    tscv = TimeSeriesSplit(n_splits=5)
    model = HistGradientBoostingClassifier(max_depth=4, learning_rate=0.05, max_iter=300)
    for tr, te in tscv.split(X):
        model.fit(X.iloc[tr], y.iloc[tr])
        pred = model.predict_proba(X.iloc[te])[:, 1]
        print("fold AUC:", round(roc_auc_score(y.iloc[te], pred), 3))
    model.fit(X, y)
    return model
Enter fullscreen mode Exit fullscreen mode

Time-series split, never random — random leaks the future and inflates AUC.

Layer 3 — Greeks rules engine

def decide(prob_up, greeks, max_capital, risk_per_trade=0.01):
    if not (0.58 <= prob_up <= 0.80):
        return {}
    if greeks["dte"] <= 1:
        return {}
    if abs(greeks["vega"]) > 8.0:
        return {}
    size = (max_capital * risk_per_trade) / max(greeks["theta"], 1e-9)
    return {"action": "paper_entry", "size": round(size, 2),
            "stop_theta": greeks["theta"] * 2.5}
Enter fullscreen mode Exit fullscreen mode

Backtest (pandas vectorized)

def backtest(signals: pd.DataFrame, fees_bps=2.0) -> float:
    s = signals.copy()
    s["position"] = ((s["prob_up"] >= 0.60) & (s["dte"] > 1)).astype(int)
    s["pnl"] = s["position"] * (s["delta"] * s["spot_ret"] * 100
                                - s["theta"] + s["prob_up"] - 0.5)
    s["pnl"] -= (s["position"] * fees_bps / 10000.0)
    return s["pnl"].sum()
Enter fullscreen mode Exit fullscreen mode

VHSI (Hang Seng Volatility Index) regime filter

  • Vol low: favor longer-DTE structures.
  • Vol mid: baseline.
  • Vol high: halve size, widen band.

Worked Example (Hang Seng Index (HSI), strike 18000.0, DTE 15)

Suppose the model outputs prob_up = 0.63, Greeks delta=0.5, theta=-40.0, vega=95.0. Capital 10,000 HKD, risk 1%:

  1. risk_per_trade = 0.01.
  2. `size = (10_000 * 0.01) / max(40.0, 1e-9) = 2.5 HKD budget.
  3. Stop at theta * 2.5 = -100.0.
  4. Open only if dte > 1 and vega <= 8 — both satisfied. Result: paper_entry, size 2.5 HKD, stop at -100.0 theta.

Market Data Sources (SFC)

  • HKEX: official options chain, IV surface, OI.
  • VHSI (Hang Seng Volatility Index): regime signal.
  • SFC publications: conduct rules, product governance.
  • Broker APIs (Futu, Tiger, HSBC): forward HKEX prices.

Local Market Structure

The HSI is mainland-sensitive; cross-border policy headlines spike VHSI fast, so the vega block in the rules engine is essential here.

Position Sizing Calculator (runnable)

`python
def position_size(capital, risk_pct, theta, vega, vega_cap=8.0):
base = capital * risk_pct
if abs(vega) > vega_cap:
base *= vega_cap / abs(vega)
lots = base / max(abs(theta), 1e-9)
return round(lots, 2)

if name == "main":
print("calm :", position_size(10000, 0.01, 40.0, 3.0))
print("stress:", position_size(10000, 0.01, 40.0, 24.0))
`

The stress case shows the calculator automatically cuts exposure when vega blows past the cap — exactly the behaviour the rules engine enforces.

Strategy Variations

  • Vertical spread: caps max loss, favourite in high-vega regimes.
  • Calendar spread: profits from term-structure slope.
  • Iron condor: collects theta, watch gamma at short strikes.
  • Naked long call/put: highest convex payoff, only with prob_up 0.70-0.80 and dte > 5.

Walk-Forward Evaluation

A single TimeSeriesSplit is honest, but production needs walk-forward: retrain on a rolling window, test on the next, slide forward. This catches model decay that static splits hide.

Feature Importance

On options data the ranking is usually: (1) theta_per_delta, (2) iv_skew, (3) moneyness, (4) vol-index, (5) pcr. If your model ranks oi or volume first, suspect leakage — those are post-hoc liquidity, not predictive. Drop them and re-check.

Glossary

  • Delta: directional exposure per 1 unit of underlying.
  • Gamma: rate of change of delta; high gamma = convex risk.
  • Theta: daily time decay; cost of holding.
  • Vega: sensitivity to implied volatility; dominant risk in stress.
  • IV skew: strike IV minus ATM IV; cheapness signal.
  • PCR: put-call ratio; sentiment extreme indicator.
  • DTE: days to expiry; hard stop before assignment.
  • Moneyness: strike / spot - 1; negative = ITM, positive = OTM.

Deployment Checklist

Before any paper trade:

  • [ ] TimeSeriesSplit AUC printed, not random-split.
  • [ ] Walk-forward mean AUC stable across windows.
  • [ ] Feature importance sane (no leakage features ranked top).
  • [ ] Rules engine hard limits active (dte, vega, prob band).
  • [ ] Backtest includes fees and theta accrual.
  • [ ] Position size calculator wired to the rules layer.

Monitoring and Alerting (production hygiene)

A model that is not monitored decays silently. Wire three alerts:

  1. AUC drift: retrain daily; if rolling 5-day AUC drops more than 3% from the 30-day mean, halt paper entries and flag for review.
  2. Fill divergence: compare expected mid (from your predicted probability band) to actual fill; a persistent gap means the broker quote is wider than your assumption — tighten spread_pct.
  3. Vol-regime flip: if the local volatility index moves more than 1.5 standard deviations in a session, force the risk layer into the high-vol branch regardless of model output.

These three alerts catch the failures that a backtest, by definition, cannot — because the backtest already assumed the regime you are now living in.

Regime-Switching Model (optional upgrade)

Instead of a single classifier, train two: one on low-vol windows, one on high-vol windows, and route each live row to the matching model by the current volatility-index z-score. This typically adds 1-3 AUC points over a monolithic model because the IV-skew feature behaves oppositely in the two regimes. The routing code is trivial:

python
def route(model_low, model_high, row, vol_z):
m = model_high if vol_z > 0 else model_low
return m.predict_proba(row)[:, 1]

Keep the rules engine identical — the routing only changes which probability the engine receives, never the hard limits.

Why This Beats a Black-Box Net

A gradient-boosting model on ten transparent features is explainable: you can show a regulator or a client exactly which input moved the decision. A deep net cannot. For retail options — where a single bad fill ends the week — explainability is not a nicety, it is the difference between surviving a regime change and blowing up inside it. Build the boring model, enforce the boring risk layer, and let compounding do the rest.

Common mistakes

  1. Random split on time-series.
  2. Ignoring bid/ask spread.
  3. Naked short options for "high probability".
  4. Overfitting IV skew to one regime.
  5. No position sizing.

Weekly routine

  • Mon: rebuild features, retrain if AUC drift > 3%.
  • Tue–Thu: paper-trade, log fills vs prediction.
  • Fri: review false positives, tighten rules.

FAQ

Q1. Do I need a neural network for Hang Seng Index (HSI) options?
No. Gradient-boosting on well-built features typically matches or beats nets on tabular options data and is easier to audit.

Q2. Is this legal under SFC rules?
Building and paper-trading your own model is legal. Live automation triggers broker review. Consult a compliance professional.

Q3. How much capital per trade?
<=1% of capital per trade, scaled by Greeks. Never risk what you can't lose.

Q4. Can I run this from a phone?
Yes. Pure Python/pandas runs on Termux or a Raspberry Pi.

Q5. Biggest edge — model or risk layer?
The risk layer. A mediocre model with strict Greek limits survives; a great model without them does not.

Footer

Shakti Tiwari — Options Trader, XGBoost Expert.
Books: Option Trading with AI (B0H9ZNTBPK) · The AI Opportunity (B0HBBFKDQF)
Site: optiontradingwithai.in · Free help: shaktitiwari715@gmail.com
Dev.to: @shaktitiwari · X: @shaktitiwari

Top comments (0)