Intraday Trading with XGBoost

Intraday trading presents unique challenges: higher noise, more transactions, and tight timing. XGBoost can capture intraday patterns but requires careful feature engineering and realistic expectations. The key is not predicting exact prices but identifying probability edges.

Data Requirements

  • 5-minute OHLCV bars for Nifty 50
  • Option chain snapshots every 15 minutes
  • Intraday volume profiles
  • Market breadth data (advance/decline)
  • VIX intraday changes

Intraday Feature Engineering

def create_intraday_features(df):
    """Create features for intraday trading."""
    features = pd.DataFrame(index=df.index)
    
    # Price action
    features['bar_range'] = (df['high'] - df['low']) / df['close']
    features['body_ratio'] = abs(df['close'] - df['open']) / (df['high'] - df['low'] + 1e-10)
    features['upper_shadow'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']
    features['lower_shadow'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']
    
    # Volume patterns
    features['volume_surge'] = df['volume'] / df['volume'].rolling(20).mean()
    features['vwap_deviation'] = (df['close'] - df['vwap']) / df['vwap']
    
    # Time-based features
    features['hour'] = df.index.hour
    features['minute'] = df.index.minute
    features['is_open'] = (features['hour'] == 9) & (features['minute'] < 30)
    features['is_close'] = (features['hour'] == 15) & (features['minute'] >= 15)
    
    # Momentum
    features['return_3bars'] = df['close'].pct_change(3)
    features['return_6bars'] = df['close'].pct_change(6)
    features['return_12bars'] = df['close'].pct_change(12)  # 1 hour
    
    # Volatility
    features['realized_vol'] = df['close'].pct_change().rolling(12).std()
    features['vol_ratio'] = features['realized_vol'] / features['realized_vol'].rolling(60).mean()
    
    return features

Walk-Forward Results

5-minute bars on Nifty 50 (2024-2025):

  • Walk-forward AUC: 0.54-0.58
  • Trades per day: 8-15
  • Win rate: 52-56%
  • Average holding period: 30-60 minutes

Signal Generation

def generate_signals(model, features, threshold=0.58):
    """Generate trading signals."""
    proba = model.predict_proba(features)[:, 1]
    
    signals = pd.Series(index=features.index, dtype=str)
    signals[proba > threshold] = 'BUY'
    signals[proba < (1 - threshold)] = 'SELL'
    signals[(proba >= 1 - threshold) & (proba <= threshold)] = 'HOLD'
    
    return signals

Risk Management

  • Position size: Maximum 2% of capital per trade
  • Stop loss: 0.3% from entry (for 5-minute bars)
  • Take profit: 0.5% from entry (1.67:1 reward-to-risk)
  • Max daily loss: 1% of capital
  • Max concurrent trades: 1

Transaction Costs Matter

For intraday trading, transaction costs dominate:

  • Brokerage: ~0.03% per trade
  • STT: 0.025% on sell side
  • Exchange charges: ~0.01%
  • Total round trip: ~0.1%

Your average trade must exceed 0.1% to be profitable. With 0.5% take profit and 55% win rate: expected value per trade = (0.55 × 0.5%) - (0.45 × 0.3%) - 0.1% = 0.04% (profitable).

SEBI Disclaimer

Intraday trading in options involves substantial risk of loss. This article is for educational purposes only. The author, Shakti Tiwari, is NISM-Series-XII certified. Past performance does not guarantee future results. Trade with money you can afford to lose.

Snapshot Versus Bar Features

5-minute models survive or die on whether features seen at decision time match what the bar eventually printed:

  • Use only completed-bar statistics: the 09:20 bar's action is unknown until all four prints land; a feature that peeks at the bar's own close is leakage.
  • Snapshot features (order imbalance, top-of-book depth, spread) mark the state you could actually trade and should be stamped with the exact timestamp.
  • Rescale the bar features by rolling 20-bar volatility so a 50-point move means the same thing in a slow July morning as a nervous pre-election one.

Session Segments: The Three Personalities of One Day

Indian intraday splits into seasons, and a single model trained over the whole day averages three different games:

  • Open (09:15-10:00): overnight-gap resolution, auction particointment, high true range; vol knives and gap-fill trades dominate.
  • Core (10:00-14:15): index drift, mean-reversion, event lulls; liquidity deepens and patterns behave most "textbook".
  • Close (14:15-15:30): expiry-week positioning, gamma chasing, single-name institutional prints; max-pain gravity and final-hour fading colour everything.

Train one model per segment and gate by clock; segment models routinely beat a single joint model at equal feature budget.

The Volatility Fingerprint as a Feature Family

Realised vol at multiple scales is the best free predictor of next-bar behaviour:

  • Vol over the last 1, 3 and 5 bars; the ratio between them flags acceleration or compression.
  • Range-to-twap ratio (the bar's high-low captured against its volume-weighted average) reads conviction versus chop.
  • Post-gap vol, post-news vol: add an indicator for whether the last bar followed an overnight gap, and the model learns that first-hour volatility decays differently.

Execution Assumptions That Must Be Honest

Intraday P&L lives on executable assumptions:

  • Model limit orders on the touch rather than market fills; the model's high-probability trade is the one a maker order can actually participate in.
  • Charge spread on every marketable order and queue-position logic on maker entries; a backtest that adds 1 tick of slippage fails most 5-minute edges instantly.
  • Respect MIS leverage caps: the position value that fits a margin account, not a theoretical nav, is the tradable size.

Intraday Cost Math in Rupees

Before worshipping any setup, price the friction of the actual 5-minute round trip on a Nifty futures lot:

  • Brokerage, STT, exchange charges, GST, stamp duty and SEBI fees total near ₹150-250 for a 1-lot round trip on most discount brokers.
  • A strategy earning 0.15% per 5-minute trade needs to clear that ledger every single time; 40 trades a day means the cost column is the strategy's EBITDA, not its gross.
  • Intraday strategies that lose to their own cost model are the majority; the rare ones that win share three traits: 1-2 trades a day, defined stops, and fill-logs feeding the cost model weekly.

XGBoost for 5-minute bars works when the feature feed is leak-free, the session is segmented, and the execution assumptions match broker reality. The edge in intraday ML is not the tree's cleverness at 09:47; it is the discipline that the model trades only what the fills, the fees and the margin genuinely allow.