Instruments: Crude Oil futures (CL) and 10-Year T-Note futures (ZN) Goal: Forecast next-day realized volatility, convert each forecast into a dollar-risk-targeted position size, and backtest whether the sizing rules kept risk disciplined.
data/fetch_data.py— fetches CL and ZN history fromyfinanceand computes log returns + realized volatility.models/garch_model.py— rolling GARCH(1,1) forecasts.models/features.py— time-series features for the ML model.models/ml_model.py— LightGBM regression with walk-forward forecasting.risk/position_sizing.py— volatility-targeted sizing engine with a 10-lot cap.risk/fair_value.py— simple cost-of-carry fair-value flag module.backtest/backtest_engine.py— builds the simulation frame for both models.backtest/metrics.py— realized-risk metrics, breach rate, RMSE, and drawdown utilities.dashboard/app.py— Streamlit dashboard skeleton for CL and ZN.requirements.txt— dependencies for running the project.
cd /Users/manangarg/Desktop/Finance_Project/Volatility-Adaptive-Position-Sizing
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
python3 -m streamlit run dashboard/app.pyIf you prefer to validate the pipeline manually first, use:
from data.fetch_data import load_or_fetch, compute_realized_vol
from backtest.backtest_engine import build_backtest_frame
df = load_or_fetch('CL=F', period='3y')
df = compute_realized_vol(df)
bt = build_backtest_frame(df, 'CL')
print(bt[['realized_vol', 'garch_vol', 'ml_vol', 'garch_size', 'ml_size']].tail())This manual check confirms the data fetcher, feature pipeline, forecasts, and sizing outputs before you build the dashboard.
This project is a risk-management system, not a directional signal.
- GARCH is the baseline volatility forecast.
- LightGBM is the ML forecast using lagged realized vol, range-based volatility, volume, and calendar features.
- The sizing rule targets a fixed dollar risk per trade (
$500) and caps positions at 10 lots. - The backtest measures whether realized risk stayed close to the target, not whether price predictions were right.
Source: yfinance for CL=F and ZN=F daily OHLCV.
The pipeline computes:
log_return= daily log return of close pricesrealized_vol= 10-day rolling annualized standard deviation of log returns
This realized volatility is the ground truth used for one-step-ahead forecasting.
- Built with
arch - Uses a rolling fit and walk-forward forecasting to avoid lookahead bias
- Forecasts the next day’s volatility from past return shocks and variance
- Uses features computed from historical data available at each time point
- Walk-forward training is used instead of random splits
- The model is trained on lagged volatility, Parkinson range, volume momentum, and calendar signals
The core sizing rule is:
position_size = target_risk_dollars / (forecast_daily_vol × price × contract_multiplier)
Where:
target_risk_dollars= $500contract_multiplier= $1,000 for both CL and ZNforecast_daily_vol= daily volatility forecast as a decimalprice= current futures price- maximum allowed lots = 10
This keeps the maximum notional risk aligned with the target and maps to a realistic TT SIM cap.
For each day in the backtest:
- compute past realized volatility and time-series features
- produce a GARCH forecast and an ML forecast using only prior data
- compute the lot size for each model
- calculate the next-day realized dollar risk for that size
- compare realized risk against the $500 target
This is a risk-discipline backtest, not a P&L direction backtest.
It means running the core code directly, not only the dashboard. A manual check ensures:
- historical data loads correctly
- realized volatility is computed correctly
- GARCH forecasts are generated in a walk-forward way
- ML features and forecast outputs line up
- position size recommendations are produced for both models
That gives confidence in the pipeline before adding plots or UI.
These are the first validation metrics from a 3-year backtest run.
-
CL=F:- usable backtest days:
482 - GARCH RMSE vs realized vol:
0.4338 - ML RMSE vs realized vol:
0.0756 - GARCH mean absolute realized-risk deviation:
1,290.67 - ML mean absolute realized-risk deviation:
500.00 - both models currently produce conservative sizing in the initial validation
- usable backtest days:
-
ZN=F:- usable backtest days:
483 - GARCH RMSE vs realized vol:
0.0459 - ML RMSE vs realized vol:
0.0021 - GARCH mean absolute realized-risk deviation:
6,251.998 - ML mean absolute realized-risk deviation:
500.00 - initial sizing remains conservative, with the latest ML recommendation at 0 lots
- usable backtest days:
Note: these are preliminary numbers from the implementation as of this run. They should be updated after improving the ML walk-forward training and the size calculation logic.
- improve the ML feature set and walk-forward split
- compute full backtest metrics including drawdown and tolerance breach rate
- add the fair-value flag to the dashboard panels
- make the dashboard charts and model comparison tables interactive
- document the final findings and interview walkthrough clearly
- keep CL and ZN separate in all analysis
- keep the 10-lot cap explicit
- the fair-value module is a secondary signal, not the main sizing engine
- any resume bullet should be backed by the actual final backtest run