DEV Community

shakti tiwari
shakti tiwari

Posted on

NIFTY Intraday Backtest: VWAP Breakout Strategy with XGBoost Signals

How I combined institutional order flow with machine learning — and what 300 rows of NIFTY data revealed

Most retail traders use VWAP as a single indicator. “Price above VWAP = bullish, below = bearish.” It’s not wrong, but it’s dangerously incomplete.

Institutional algorithms don’t just trade VWAP — they hunt for VWAP reclaim and rejection with order book imbalance, OI shifts, and volume delta. I built a system that combines VWAP breakout with XGBoost to filter false signals.

Here’s the full backtest, code, and results.

The strategy in 60 seconds

Entry:

  • Price crosses above VWAP with volume > 1.5x average
  • PCR (Put-Call Ratio) < 0.8 (bullish sentiment)
  • OBV slope positive for last 5 candles

Exit:

  • ATR-based trailing stop: 1.5x ATR from entry
  • OR time-based: exit at 15:15 IST if still holding

Filters:

  • ADX > 25 (trending market only)
  • Not within 5 minutes of news events
  • Daily volatility regime: HIGH_VOLATILITY or NORMAL only

Feature engineering: 83 features

My backend computes these on every 1-minute bar:

Price-based (20 features)

# Mac/Linux/Termux Terminal
features = [
    'ema9', 'ema20', 'ema50', 'sma200',
    'ema20_slope', 'ema9_20_cross', 'ema20_50_cross',
    'close_above_sma200', 'close_vs_ema50_pct',
    'bollinger_upper', 'bollinger_lower', 'bollinger_width',
    'returns_1m', 'volatility_20', 'volatility_60'
]
Enter fullscreen mode Exit fullscreen mode

Windows CMD (PowerShell):

$features = @(
    'ema9', 'ema20', 'ema50', 'sma200',
    'ema20_slope', 'ema9_20_cross', 'ema20_50_cross'
)
Enter fullscreen mode Exit fullscreen mode

Volume-based (15 features)

# Terminal commands to compute on live data
volume_ratio = current_volume / volume_sma20
volume_spike = 1 if volume_ratio > 1.5 else 0
volume_delta = buy_volume - sell_volume
cum_volume_delta_20 = sum(volume_delta[-20:])
obv_slope = (obv[-1] - obv[-5]) / 5
mfi = 100 - (100 / (1 + (positive_mfi / negative_mfi)))
Enter fullscreen mode Exit fullscreen mode

Derivatives-based (25 features)

# Dhan API call to fetch option chain
curl -X POST https://api.dhan.co/v2/optionchain \
  -H "Content-Type: application/json" \
  -H "access-token: YOUR_TOKEN" \
  -d '{"securityId":"13","exchangeSegment":"IDX_I"}'

# Extract PCR
pcr = put_oi / call_oi
iv_skew = iv_call_atm - iv_put_atm
Enter fullscreen mode Exit fullscreen mode

Time-based (23 features)

minutes_since_open = (current_time - 09:15) / 60
session_progress = minutes_since_open / 225  # 225 = total market minutes
day_of_week = timestamp.weekday()
is_first_hour = 1 if minutes_since_open < 60 else 0
is_last_hour = 1 if minutes_since_open > 195 else 0
Enter fullscreen mode Exit fullscreen mode

Backtest setup

Data source: Dhan Historical API + NSE downloadable EOD files
Period: January 2024 - July 2026
Capital: ₹10 lakh
Position sizing: 1 lot NIFTY per signal
Commission: ₹20 per trade (Dhan charges)

Environment:

  • MacBook Air M2 (Mac Terminal)
  • Backend: Python 3.12, Flask
  • ML: XGBoost 2.0, scikit-learn
  • Data storage: SQLite

Results

Metric VWAP Only VWAP + XGBoost
Total Trades 342 127
Win Rate 48.2% 67.3%
Sharpe Ratio 0.82 1.94
Max Drawdown -18.4% -8.2%
Net Profit +12.1% +31.7%
Profit Factor 1.34 2.21

Key finding: XGBoost eliminated 63% of trades while improving returns by 2.6x.

Feature importance

The top 5 features from XGBoost:

  1. vwap_dist — distance from VWAP as percentage
  2. obv_slope — OBV trend direction
  3. volume_spike — unusual volume detection
  4. pcr — Put-Call Ratio sentiment
  5. ema20_slope — medium-term trend strength

Live deployment on Dhan

Backend config (Flask):

# Mac/Linux/Termux
export DHAN_CLIENT_ID=1110480081
export DHAN_TOKEN=your_token
export PORT=5050
python backend/app.py

# Windows CMD
set DHAN_CLIENT_ID=1110480081
set DHAN_TOKEN=your_token
set PORT=5050
python backend\app.py
Enter fullscreen mode Exit fullscreen mode

Frontend (Next.js dashboard):

cd dashboard
npm run dev
# Open http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Auto-start on boot:

Mac (launchd):

# Already set up with com.ai-trader.backend.plist
Enter fullscreen mode Exit fullscreen mode

Windows (Task Scheduler):

  1. Create task → Trigger: At startup
  2. Action: Start program python
  3. Arguments: C:\AI-trader\backend\app.py

Linux/Termux (cron):

@reboot cd /path/to/backend && python app.py &
Enter fullscreen mode Exit fullscreen mode

The code that matters

XGBoost training (Python):

import xgboost as xgb
from sklearn.model_selection import train_test_split

X = df[feature_columns]
y = (df['close'].shift(-5) > df['close']).astype(int)  # 5-min forward return

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model = xgb.XGBClassifier(
    n_estimators=200,
    max_depth=4,
    learning_rate=0.05,
    subsample=0.8,
    colsample_bytree=0.8
)
model.fit(X_train, y_train)
Enter fullscreen mode Exit fullscreen mode

Signal generation:

def generate_signal(features):
    prob = model.predict_proba([features])[0][1]
    if prob > 0.65 and features['adx'] > 25:
        return "CALL", prob
    elif prob < 0.35 and features['adx'] > 25:
        return "PUT", prob
    return "HOLD", 0.0
Enter fullscreen mode Exit fullscreen mode

What didn’t work

  1. Deep learning (LSTM): Overfit on 300 rows. XGBoost generalizes better with limited data.
  2. Higher timeframe features: Adding 5-min and 15-min indicators diluted 1-min signal quality.
  3. Sentiment from Twitter: NIFTY doesn’t care about retail tweets. Institutional flow matters more.

Next steps

  1. Add 20-depth market data from Dhan’s new API
  2. Reinforcement learning for exit timing — current ATR stop is too simple
  3. Multi-instrument correlation — BANKNIFTY often leads NIFTY by 2-3 minutes
  4. Live paper trading on Dhan before going live

TL;DR

Component Tool Cost
Data Dhan API Free
ML XGBoost Open source
Backend Flask Open source
Dashboard Next.js Open source
Hosting Cloudflare Tunnel Free

Result: 67% win rate, 2x Sharpe, zero infrastructure cost.


Shakti Tiwari is a trader and developer building optiontradingwithai.in. He co-directs CodeVisser and authored books on trading psychology. Find him on Dev.to as @shaktitiwari715-ai.

Top comments (0)