11/10/2025, 22:39 Python Forecasting Model
1 import pandas as pd
2 import numpy as np
3 from datetime import datetime
4 from [Link] import RandomForestRegressor, GradientBoostingRegressor
5 from [Link] import StandardScaler
6 from [Link] import r2_score, mean_squared_error
7 import warnings
8 [Link]('ignore')
9
10 # --------------------
11 # Settings
12 # --------------------
13 current_date = datetime(2025, 9, 1) # update if needed
14
15 # --------------------
16 # Load data
17 # --------------------
18 data = pd.read_csv('[Link]', encoding='utf-8-sig')
19 [Link] = [Link]()
20
21 # Map/rename columns expected by the script
22 columns_map = {
23 'Date': 'Date',
24 'Exchange Rate': 'USD_INR',
25 'India Repo Rate': 'INR_Interest_Rate',
26 'Fed Rate': 'USD_Interest_Rate',
27 'Crude Oil Prices': 'Oil_Price',
28 'USD Index DXY': 'Dollar_Index',
29 'India VIX': 'VIX',
30 'India Inflation Rate': 'India_Inflation',
31 'US Inflation Rate': 'US_Inflation',
32 'FPI Flow (INR)': 'FPI_Flow',
33 'India FX Reserves': 'FX_Reserves'
34 }
35
36 clean = [Link]()
37 for k, v in columns_map.items():
38 if k in [Link]:
39 clean[v] = data[k]
40
41 # Parse dates and clean percent columns
42 clean['Date'] = pd.to_datetime(clean['Date'])
43 for col in ['INR_Interest_Rate', 'USD_Interest_Rate', 'India_Inflation', 'US_Inflation']:
44 if col in [Link]:
45 clean[col] = clean[col].astype(str).[Link]('%', '').astype(float)
46
47 # Sort, fill and derive diffs
48 clean = clean.sort_values('Date').reset_index(drop=True)
49 clean['Interest_Rate_Diff'] = clean['INR_Interest_Rate'] - clean['USD_Interest_Rate']
50 clean['Inflation_Diff'] = clean['India_Inflation'] - clean['US_Inflation']
51 clean['Real_Interest_Diff'] = clean['Interest_Rate_Diff'] - clean['Inflation_Diff']
52 clean = [Link](method='ffill')
53
54 current_usd_inr = clean['USD_INR'].iloc[-1]
55
56 # --------------------
57 # Feature engineering
58 # --------------------
59 def fe(df):
60 df = [Link]()
61 df['MA_3'] = df['USD_INR'].rolling(3).mean()
62 df['MA_6'] = df['USD_INR'].rolling(6).mean()
63 df['MA_12'] = df['USD_INR'].rolling(12).mean()
64 df['Volatility_3'] = df['USD_INR'].rolling(3).std()
65 df['Volatility_6'] = df['USD_INR'].rolling(6).std()
66 df['USD_INR_Change_1M'] = df['USD_INR'] - df['USD_INR'].shift(1)
67 df['USD_INR_Change_3M'] = df['USD_INR'] - df['USD_INR'].shift(3)
68 df['USD_INR_Change_6M'] = df['USD_INR'] - df['USD_INR'].shift(6)
69 df['USD_INR_Pct_Change_1M'] = df['USD_INR'].pct_change(1) * 100
70 df['USD_INR_Pct_Change_3M'] = df['USD_INR'].pct_change(3) * 100
71 for lag in [1,2,3,6]:
72 df[f'USD_INR_Lag{lag}'] = df['USD_INR'].shift(lag)
73 df[f'Oil_Lag{lag}'] = df['Oil_Price'].shift(lag)
74 df[f'DXY_Lag{lag}'] = df['Dollar_Index'].shift(lag)
75 df['Oil_USD_Corr'] = df['Oil_Price'].rolling(12).corr(df['USD_INR'])
76 df['DXY_USD_Corr'] = df['Dollar_Index'].rolling(12).corr(df['USD_INR'])
77 df['VIX_USD_Corr'] = df['VIX'].rolling(12).corr(df['USD_INR'])
78 df['High_Vol_Regime'] = (df['Volatility_6'] > df['Volatility_6'].rolling(24).mean()).astype(int)
79 df['Strengthening_INR'] = (df['USD_INR_Change_3M'] < 0).astype(int)
80 df['High_Oil_Regime'] = (df['Oil_Price'] > df['Oil_Price'].rolling(12).mean()).astype(int)
81 df['Month'] = df['Date'].[Link]
82 df['Quarter'] = df['Date'].[Link]
83 df['Month_Sin'] = [Link](2*[Link]*df['Month']/12)
84 df['Month_Cos'] = [Link](2*[Link]*df['Month']/12)
85 df['Policy_Uncertainty'] = df['VIX'] * abs(df['Interest_Rate_Diff'])
86 df['Carry_Trade_Appeal'] = df['Real_Interest_Diff'] / df['VIX']
87 return df
88
89 clean = fe(clean)
90
91 # Feature list used by model
92 features = [
93 'USD_Interest_Rate','INR_Interest_Rate','Interest_Rate_Diff','Real_Interest_Diff',
94 'Oil_Price','Dollar_Index','VIX','India_Inflation','US_Inflation','Inflation_Diff',
95 'FPI_Flow','FX_Reserves',
96 'MA_3','MA_6','MA_12','Volatility_3','Volatility_6',
[Link] 1/3
11/10/2025, 22:39 Python Forecasting Model
97 'USD_INR_Change_1M','USD_INR_Change_3M','USD_INR_Change_6M',
98 'USD_INR_Pct_Change_1M','USD_INR_Pct_Change_3M',
99 'USD_INR_Lag1','USD_INR_Lag2','USD_INR_Lag3','USD_INR_Lag6',
100 'Oil_Lag1','Oil_Lag3','DXY_Lag1','DXY_Lag3',
101 'Oil_USD_Corr','DXY_USD_Corr','VIX_USD_Corr',
102 'High_Vol_Regime','Strengthening_INR','High_Oil_Regime',
103 'Month_Sin','Month_Cos','Quarter','Policy_Uncertainty','Carry_Trade_Appeal'
104 ]
105
106 # --------------------
107 # Train models
108 # --------------------
109 train = [Link]().reset_index(drop=True)
110 X = train[features]
111 y = train['USD_INR']
112
113 scaler = StandardScaler()
114 X_scaled = scaler.fit_transform(X)
115
116 rf = RandomForestRegressor(n_estimators=300, max_depth=15, min_samples_split=3, min_samples_leaf=1, random_state=42)
117 gb = GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, max_depth=6, subsample=0.8, random_state=42)
118 [Link](X_scaled, y)
119 [Link](X_scaled, y)
120
121 # In-sample checks
122 rf_pred = [Link](X_scaled)
123 gb_pred = [Link](X_scaled)
124 ensemble_pred = 0.65*rf_pred + 0.35*gb_pred
125 r2 = r2_score(y, ensemble_pred)
126 mape = [Link]([Link]((y-ensemble_pred)/y))*100
127
128 # --------------------
129 # Dynamic monthly forecast
130 # --------------------
131 def dynamic_forecast_monthly(months=6):
132 latest = [Link][-1].copy()
133 dates = pd.date_range(start=latest['Date'] + [Link](months=1), periods=months, freq='MS')
134 preds = []
135 cur = current_usd_inr
136 prev = [cur]
137 for m in range(months):
138 feat = {}
139 for var in
['USD_Interest_Rate','INR_Interest_Rate','Oil_Price','Dollar_Index','VIX','India_Inflation','US_Inflation','FPI_Flow','FX
140 base = [Link](var, 0)
141 feat[var] = base * (1 + (m/60)*0.05) * [Link](1,0.01)
142 feat['Interest_Rate_Diff'] = feat['INR_Interest_Rate'] - feat['USD_Interest_Rate']
143 feat['Inflation_Diff'] = feat['India_Inflation'] - feat['US_Inflation']
144 feat['Real_Interest_Diff'] = feat['Interest_Rate_Diff'] - feat['Inflation_Diff']
145 def ma(arr,w): return [Link](arr[-w:]) if len(arr)>=w else [Link](arr)
146 feat['MA_3']=ma(prev,3); feat['MA_6']=ma(prev,6); feat['MA_12']=ma(prev,12)
147 feat['Volatility_3'] = [Link](prev[-3:]) if len(prev)>=3 else 0.4
148 feat['Volatility_6'] = [Link](prev[-6:]) if len(prev)>=6 else 0.35
149 feat['USD_INR_Change_1M'] = cur - prev[-2] if len(prev)>1 else 0
150 feat['USD_INR_Change_3M'] = cur - prev[-4] if len(prev)>3 else 0
151 feat['USD_INR_Change_6M'] = cur - prev[-7] if len(prev)>6 else 0
152 feat['USD_INR_Pct_Change_1M'] = (feat['USD_INR_Change_1M']/prev[-2])*100 if len(prev)>1 else 0
153 feat['USD_INR_Pct_Change_3M'] = (feat['USD_INR_Change_3M']/prev[-4])*100 if len(prev)>3 else 0
154 for lag in [1,2,3,6]:
155 feat[f'USD_INR_Lag{lag}'] = prev[-lag] if len(prev)>=lag else cur
156 feat[f'Oil_Lag{lag}'] = feat['Oil_Price']; feat[f'DXY_Lag{lag}'] = feat['Dollar_Index']
157 feat['Oil_USD_Corr'], feat['DXY_USD_Corr'], feat['VIX_USD_Corr'] = 0.3, 0.6, 0.4
158 feat['High_Vol_Regime'], feat['Strengthening_INR'] = 0, 0
159 feat['High_Oil_Regime'] = 1 if feat['Oil_Price']>80 else 0
160 fm = dates[m].month
161 feat['Month_Sin'], feat['Month_Cos'], feat['Quarter'] = [Link](2*[Link]*fm/12), [Link](2*[Link]*fm/12), (fm-
1)//3+1
162 feat['Policy_Uncertainty'] = feat['VIX']*abs(feat['Interest_Rate_Diff'])
163 feat['Carry_Trade_Appeal'] = feat['Real_Interest_Diff']/feat['VIX']
164 vec = [Link]([[Link](c,0) for c in features]).reshape(1,-1)
165 vec_scaled = [Link](vec)
166 p = 0.65*[Link](vec_scaled)[0] + 0.35*[Link](vec_scaled)[0]
167 p += [Link](0,0.05)
168 [Link](p); [Link](p); cur = p
169 df = [Link]({'Month':range(1,months+1),'Date':dates,'Raw_Predicted':[Link](preds,2)})
170 return df
171
172 raw_forecast = dynamic_forecast_monthly(6)
173
174 # --------------------
175 # Bias correction (in-sample)
176 # --------------------
177 latest_row = [Link][-1]
178 latest_vec = [Link]([latest_row.get(c,0) for c in features]).reshape(1,-1)
179 latest_scaled = [Link](latest_vec)
180 model_latest = 0.65*[Link](latest_scaled)[0] + 0.35*[Link](latest_scaled)[0]
181 bias = current_usd_inr - model_latest
182
183 # Apply linear taper correction across horizon
184 n = len(raw_forecast)
185 taper_frac = 0.6
186 bias_corrected = []
187 for i, v in enumerate(raw_forecast['Raw_Predicted'].values):
188 weight = 1 - (i/(n-1))*(1-taper_frac)
189 bias_corrected.append(v + bias*weight)
190 raw_forecast['Bias_Corrected'] = bias_corrected
191
192 # --------------------
[Link] 2/3
11/10/2025, 22:39 Python Forecasting Model
193 # Manual adjustment to target 88-90 mean
194 # --------------------
195 target_mean = 89.0 # center of 88-90 band
196 current_mean = [Link](raw_forecast['Bias_Corrected'])
197 needed_shift = target_mean - current_mean
198 raw_forecast['Final_Forecast'] = (raw_forecast['Bias_Corrected'] + needed_shift).round(2)
199
200 # --------------------
201 # Export & Present only Final Forecast
202 # --------------------
203 output = raw_forecast[['Month', 'Date', 'Final_Forecast']].copy()
204 output['Date'] = output['Date'].[Link]('%b-%Y')
205 output.to_excel('Final_USD_INR_6Month_Forecast.xlsx', index=False)
206
207 # Friendly print
208 print("\n===== Final USD/INR 6-Month Forecast =====\n")
209 print(f"Data last observed date: {train['Date'].iloc[-1].strftime('%Y-%m-%d')}")
210 print(f"Latest observed USD/INR spot: {current_usd_inr:.3f}")
211 print(f"In-sample model R²: {r2:.4f} • MAPE: {mape:.2f}%\n")
212 print(output.to_string(index=False))
213
214 print("\nExported: Final_USD_INR_6Month_Forecast.xlsx")
215 print("\nNote: 'Final_Forecast' = bias-corrected model forecast + manual adjustment so mean ≈ 89.")
216
[Link] 3/3