Foreign Exchange Intervention Rules for Central Banks: A Risk-Based Framework#

This notebook replicates the tables and the charts of the IMF WP on Foreign Exchange Intervention Rules for Central Banks: A Ris-Based Framework

It uses a Python package that I have written, DistGARCH, also available in this Github folder, with the public FX intervention data from the Banco Mexico. DistGARCH is based on the ARCH package of Kevin Sheppard.

You can use the code for non-commercial applications, providing that you cite the IMF Working Paper
Lafarguette, R. and Veyrune, R. (2021) Foreign Exchange Intervention Rules for Central Banks: A Ris-Based Framework, IMF Working Paper no 2021032 https://www.imf.org/en/Publications/WP/Issues/2021/02/12/Foreign-Exchange-Intervention-Rules-for-Central-Banks-A-Risk-based-Framework-50081

Author: Romain Lafarguette, June 2021 If you have any question, please contact me via Github or rlafarguette “at” imf “dot” org

Preamble#

# System paths
import os, sys

# Global modules
import importlib                                        # Operating system
import pandas as pd                                     # Dataframes
import numpy as np                                      # Numeric Python
import datetime                                         # Dates
import arch                                             # ARCH/GARCH models

# Functional imports
from datetime import datetime as date                   # Short date function
from dateutil.relativedelta import relativedelta        # Dates manipulation 

# ARCH package functional imports
from arch.univariate import (ARCH, GARCH, EGARCH, EWMAVariance, # Vol process
                             FixedVariance, RiskMetrics2006) 
from arch.univariate import (Normal, StudentsT, # Distribution of residuals
                             SkewStudent, GeneralizedError)

# Local modules
from varfxi.quantileproj import QuantileProj
from varfxi.distGARCH import DistGARCH

# Graphics
import matplotlib.pyplot as plt                         # Graphical package  
import seaborn as sns                                   # Graphical tools

# Graphics options
plt.rcParams["figure.figsize"] = 25,15
#plt.rcParams["figure.dpi"] = 600
sns.set(style='white', font_scale=4, palette='deep', font='serif') 

# Pandas options
pd.set_option('display.max_rows', 50)
pd.set_option('display.max_columns', 10)

# Warnings management
import warnings
warnings.filterwarnings("ignore")

data_folder = "./data/"
output_folder = "./output/"
def logret(series):
    return(np.log(series/series.shift(1)))

Figure 1: Conditional VaR and density (diagram)#

# Read the data for Mexico
macro_p = os.path.join(data_folder, 'macro_data.csv')
dm = pd.read_csv(macro_p, parse_dates=['date'], index_col=['date'])
inter_p = os.path.join(data_folder, 'intervention_data.csv')
di = pd.read_csv(inter_p, parse_dates=['date'], index_col=['date'])
df = pd.merge(dm, di, on=['date'], how='left').sort_index().copy() # Merge
df = df[~df.index.duplicated()].copy() # Duplicated index
df.head()
mxn_usd_spot mxn_fwd_1m min_max_spread bid_ask_spread mxn_interbank_1m ... eur_usd_spot amount direction type sell_amount
date
1990-01-01 2.6835 NaN 0.000 0.0 NaN ... NaN NaN NaN NaN NaN
1990-01-02 2.6770 NaN 0.007 0.0 NaN ... NaN NaN NaN NaN NaN
1990-01-03 2.6770 NaN 0.000 0.0 NaN ... NaN NaN NaN NaN NaN
1990-01-04 2.6770 NaN 0.000 0.0 NaN ... NaN NaN NaN NaN NaN
1990-01-05 2.6770 NaN 0.000 0.0 NaN ... NaN NaN NaN NaN NaN

5 rows × 15 columns

# New macro variables
df['FX level'] = df['mxn_usd_spot'].copy()
df['FX log returns'] = 1e4*logret(df['mxn_usd_spot'])
df['Bid ask abs'] = np.abs(df['bid_ask_spread'])
df['Min max abs'] = np.abs(df['min_max_spread'])
df['Forward points first difference'] = df['mxn_fwd_1m'].diff(1)/1e2
df['Interbank rate vs Libor'] = (df['mxn_interbank_1m'] - df['usa_libor_1m']).diff(1)
df['VIX first diff'] = df['vix'].diff(1)
df['EURUSD log returns'] = 1e4*logret(df['eur_usd_spot'])
df['Oil prices log returns'] = 1e4*logret(df['oil_prices'])
# FX intervention variables
df['FX intervention in USD'] = df['sell_amount'].fillna(0)
df['fx_intervention_minprice'] = df.loc[df['type']=='min price',
                                        'sell_amount'].fillna(0)
df['fx_intervention_nominprice'] = df.loc[df['type']=='no min price',
                                          'sell_amount'].fillna(0)
df['FX intervention dummy'] = 0
df.loc[df['FX intervention in USD'] > 0, 'FX intervention dummy'] = 1
df['FX intervention dummy lag'] = df['FX intervention dummy'].shift(1)
df['Intercept'] = 1

df['FX log returns_fwd'] = df['FX log returns'].shift(-1)

clean_p = os.path.join(data_folder, 'clean_data.csv')
df.to_csv(clean_p,index=True)

Fit the GARCH model for different specifications#

# Prepare the list of variables
microstructure = ['Bid ask abs',
                  'Min max abs',
                  'Forward points first difference']

cip = microstructure + ['Interbank rate vs Libor']

eurusd = cip + ['EURUSD log returns']

vix = eurusd + ['VIX first diff']

baseline = vix + ['Oil prices log returns', 'FX intervention dummy lag']

# List of models
models_l = [microstructure, cip, eurusd, vix, baseline]
labels_l = ['Microstructure', 'CIP', 'Dollar move', 'Risk Appetite','Baseline']

Baseline GARCH Model: Fit and Forecast#

Specify the model#

dg = DistGARCH(depvar_str='FX log returns',
               data=df,
               level_str='FX level', 
               exog_l=baseline, # Defined above 
               lags_l=[1], 
               vol_model=GARCH(1,1,1),
               # ARCH(1,1), EGARCH(1,1,1), GARCH(1,1),
               # EWMAVariance(None), RiskMetrics2006(),
               dist_family=Normal(),
               # Normal(), StudentsT(), SkewStudent(), GeneralizedError()
)

Figure 2. Mexican Peso Against US Dollar#

# Plot
dg.plot.plot_description(title_level='Historical FX level',
                    title_returns='Historical FX returns',
                    title_density='Historical FX returns distribution',
                    y_label_returns='Bps')
plt.subplots_adjust(hspace=0.8)

# Save the figure
desc_f = os.path.join(output_folder, 'descriptive_plot.pdf')
plt.savefig(desc_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/bb509e7f87a735263c22f69b38f9d19f9f0faf6a13cbbb6bca27075136472144.png

Fit the Model#

# Fit the model
dgf = dg.fit()
                        AR-X - GJR-GARCH Model Results                        
==============================================================================
Dep. Variable:         FX log returns   R-squared:                       0.279
Mean Model:                      AR-X   Adj. R-squared:                  0.277
Vol Model:                  GJR-GARCH   Log-Likelihood:               -30558.5
Distribution:                  Normal   AIC:                           61145.0
Method:            Maximum Likelihood   BIC:                           61238.0
                                        No. Observations:                 5680
Date:                Sat, Apr 15 2023   Df Residuals:                     5670
Time:                        10:13:36   Df Model:                           10
                                             Mean Model                                            
===================================================================================================
                                      coef    std err          t      P>|t|        95.0% Conf. Int.
---------------------------------------------------------------------------------------------------
Const                              -5.1248      1.580     -3.243  1.184e-03       [ -8.222, -2.027]
FX l...rns[1]                      -0.0839  1.493e-02     -5.624  1.869e-08    [ -0.113,-5.469e-02]
Bid ask abs                       -53.8687     49.136     -1.096      0.273    [-1.502e+02, 42.437]
Min max abs                        68.0834     18.264      3.728  1.932e-04     [ 32.287,1.039e+02]
Forward points first difference    20.7775      2.411      8.618  6.785e-18       [ 16.052, 25.503]
Interbank rate vs Libor            38.7427      6.567      5.900  3.645e-09       [ 25.871, 51.614]
EURUSD log returns                 -0.1527  1.417e-02    -10.779  4.318e-27       [ -0.180, -0.125]
VIX first diff                     15.0417      0.669     22.485 5.814e-112       [ 13.731, 16.353]
Oil prices log returns             -0.0193  3.425e-03     -5.648  1.626e-08 [-2.605e-02,-1.263e-02]
FX intervention dummy lag          -0.9542      4.814     -0.198      0.843       [-10.389,  8.481]
                               Volatility Model                               
==============================================================================
                 coef    std err          t      P>|t|        95.0% Conf. Int.
------------------------------------------------------------------------------
omega         30.7491     10.696      2.875  4.042e-03       [  9.786, 51.713]
alpha[1]       0.1129  1.927e-02      5.860  4.630e-09     [7.513e-02,  0.151]
gamma[1]      -0.0645  1.451e-02     -4.447  8.689e-06 [-9.295e-02,-3.608e-02]
beta[1]        0.9147  1.562e-02     58.563      0.000       [  0.884,  0.945]
==============================================================================

Covariance estimator: robust

Figure 3. Conditional FX Volatility Over Time#

# Plot
dgf.plot.plot_in_cond_vol(start_date=None,
                     title='')
# Save the figure
cv_f = os.path.join(output_folder, 'conditional_vol_plot.pdf')
plt.savefig(cv_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/5869abbd7adfccc3f7e13607bff16ec2aad3774acc470a2ec57b48626d2e0003.png
# Plot
dgf.plot.plot_shocks_vol()
# Save the figure
sv_f = os.path.join(output_folder, 'shock_vol.pdf')
plt.savefig(sv_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/4b8026824fc18f1efc983039b0990349ab47809c5ae3cb1d68c40bd6d82a995f.png
<Figure size 2500x1500 with 0 Axes>

Forecast the model#

# Forecast 2020
dgfor = dgf.forecast('2020-01-01', horizon=1)

Figure 4. Out-of-sample Conditional Density (Joyplot)#

# Plot
dgfor.plot.plot_joyplot_out(
    title='',
    xlabel='',
    label_drop=15,
    xlimits_t=(-1000, 1000))

# Save the figure
joyplot_f = os.path.join(output_folder, 'joyplot.pdf')
plt.savefig(joyplot_f)
plt.show()
plt.close('all')
../../_images/6bd7bb868e0581a65a429ba556f6e3ba5a164c4e16593abe18c4d03eb998d52f.png

Figure 5. Out-of-Sample Fan Chart#

# Plot
dgfor.plot.plot_fan_chart(xticks_freq=35, title='Fan chart of predictive FX log returns \n'
                     '(1, 5, 10, 25, 75, 90, 95th conditional quantiles)')
plt.ylabel('Pips')

# Save the figure
fanchart_f = os.path.join(output_folder, 'fanchart.pdf')
plt.savefig(fanchart_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/4d7444816ce9bcea9a50aa5447fb333368db99742f0c77f9889b7f611493824d.png

Figure 6. Probability Integral Transform Test#

# Plot
dgfor.plot.plot_pit()
# Save the figure
pitchart_f = os.path.join(output_folder, 'pitchart.pdf')
plt.savefig(pitchart_f, bbox_inches='tight')
plt.show 
plt.close('all')
dgfor.plot.plot_qqplot_normalized_innovations()
# Save the figure
qqchart_f = os.path.join(output_folder, 'qqplot.pdf')
plt.savefig(pitchart_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/1cd5a2e6ea65b6fe1bcf2ce21f30acce0940ba1f7fcf75d3f42d6c2f971fbbaf.png

Figure 7. VaR FX Interventions Rule Based on a Given Information Set#

# Plot
dgfor.plot.plot_pdf_rule(fdate='2020-05-01', q_low=0.025, q_high=0.975, 
                    title='Conditional Density and Intervention Rule Based on 2020-05-01 Information', 
                    ylabel='Density', xlabel='Bps')

# Save the figure
var_rule_f = os.path.join(output_folder, 'var_rule.pdf')
plt.savefig(var_rule_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/bcd6fc1783f71b4cb0a1c12fd04f396ebc2c1e9a8c6414e356c51a2389ac149b.png

Figure 8. Conditional CDF, Out-of-sample#

# Plot
dgfor.plot.plot_conditional_cdf(q_low=0.025, q_high=0.975, size=300, title='', ylabel='Quantiles')

# Save the figure
cond_cdf_f = os.path.join(output_folder, 'conditional_cdf.pdf')
plt.savefig(cond_cdf_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/e477614c165daca3ffa46dc7b3919f8e6cfe777f3d6a6878fbab90a16d41ce12.png

Figure 9. Conditional VaR Exceedance, Out-of-Sample#

# Plot
dgfor.plot.plot_var_exceedance(qv_l=[0.025, 0.975], 
                          title_1= ('Log Returns and Conditional VaR Exceedance at 5 Percent'
                        ' \n (green square: below VaR 2.5 percent, red dot: above VaR 97.5 percent)'),
                          title_2='Corresponding FX level',
                          swap_color=True, size=300)

# Save the figure
cond_exc_f = os.path.join(output_folder, 'conditional_exceedance.pdf')
plt.savefig(cond_exc_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/87b2c69273a70c20fae10b4bd03ff01d4a73a17ffa40c6877a0146b401a880ec.png

Benchmarking exercise#

# Forecast over the Banco Mexico intervention period for benchmarking
intervention_dates_l = df.loc[df['FX intervention dummy']==1, :].index
dgmfor = dgf.forecast(np.min(intervention_dates_l) - relativedelta(days=1), horizon=1)
dgmfor.compute_pit()

# Include the forecasted results within the original dataframe
dfxi = df.merge(dgmfor.dfor, left_index=True, right_index=True, how='outer')

# New variables
dfxi['FXI'] = (dfxi['sell_amount'] >0)
cond_fxi = (dfxi['FXI']==True)

dfxi['FXI current rate'] = np.nan
dfxi.loc[cond_fxi, 'FXI current rate'] = dfxi.loc[cond_fxi, 'FX level'].copy()

dfxi['FXI current logret'] = np.nan
dfxi.loc[cond_fxi,'FXI current logret'] = dfxi.loc[cond_fxi, 'FX log returns']

dfxi['FXI cdf'] = np.nan
dfxi.loc[cond_fxi, 'FXI cdf'] = dfxi.loc[cond_fxi, 'pit']

# Differentiate between intervention types
dmfxi = dfxi.loc[np.min(intervention_dates_l):np.max(intervention_dates_l), :].copy()
dfr = dmfxi.loc[dmfxi["type"]=='min price', :].copy() # Rule
dfd = dmfxi.loc[dmfxi["type"]=='no min price', :].copy() # Discretion

Figure 10. Rule-based FX Interventions on the Mexican Peso/USD#

# With minimum price (== rule based)
# Plot
fig, (ax1, ax2) = plt.subplots(2,1)

# Log returns
ax1.plot(dmfxi.index, dmfxi['FX log returns'], color='#004C97')
ax1.scatter(dfr.index, dfr['FXI current logret'], s=100, color='green', marker='D')

ax1.set_title('',y=1.02)
ax1.set_xlabel('')
ax1.set_ylabel('Log returns', labelpad=10)

# Level
ax2.plot(dmfxi.index, dmfxi['FX level'], color='#004C97')
ax2.scatter(dfr.index, dfr['FXI current rate'], s=100, color='green', marker='D')

ax2.set_title('FX interventions and FX level (sell USD)',
              y=1.02)
ax2.set_xlabel('')
ax2.set_ylabel('FX level', labelpad=10)

plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.5)

# Save the figure
bench_minprice_f = os.path.join(output_folder, 'benchmark_minprice.pdf')
plt.savefig(bench_minprice_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/5cbf01f4d94b4337b53f02d487d9efef03252d928e8a0f4669b97bf0aa190933.png

Figure 11. Conditional CDF and Rule-based FX Interventions#

# PLot
fig, ax1 = plt.subplots(1,1)

# Local merge to have the same axis
dr = pd.merge(dmfxi['pit'], dfr['FXI cdf'], left_index=True, right_index=True, how='left')

# Level plot
ax1.plot(dr.index, 100*dr['pit'], lw=1)
ax1.scatter(dr.index, 100*dr['FXI cdf'], alpha=1, c='green', marker='D', s=300)
ax1.set_title('',
              y=1.02)
ax1.set_xlabel('')
ax1.set_ylabel('Percentiles', labelpad=10)

ax1.axhline(y=5, color='red', linestyle='--', lw=2)
ax1.axhline(y=95, color='red', linestyle='--', lw=2)
ax1.axhline(y=50, color='black', linestyle='-', lw=1)

# Add the ticks, if needed
ax1.set_yticks([]) # Remove the standard y ticks
new_t_l = [5, 95]
new_ticks_l = sorted(list(ax1.get_yticks()) + new_t_l)
extra_idx_l = [new_ticks_l.index(x) for x in new_t_l]
ax1.set_yticks(new_ticks_l) # Add new ticks

ax1.set_ylim(-5, 105)

# Save the figure
bench_minprice_cdf_f = os.path.join(output_folder, 'benchmark_minprice_cdf.pdf')
plt.savefig(bench_minprice_cdf_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/635f7d4a9675c6472586c788b59f14703a50fd6ba5a05e1f7fbc01f427faef05.png

Figure 12. Discretionary FX Interventions on the Mexican Peso/USD#

# Plot
fig, (ax1, ax2) = plt.subplots(2,1)

# Log returns
ax1.plot(dmfxi.index, dmfxi['FX log returns'], color='#004C97')
ax1.scatter(dfd.index, dfd['FXI current logret'],
            s=100, color='red',
            marker='D')

ax1.set_title('',
              y=1.02)
ax1.set_xlabel('')
ax1.set_ylabel('Log returns', labelpad=10)

# Level
ax2.plot(dmfxi.index, dmfxi['FX level'], color='#004C97')
ax2.scatter(dfd.index, dfd['FXI current rate'], s=100, color='red',
            marker='D')

ax2.set_title('FX interventions and FX level (sell USD)',
              y=1.02)
ax2.set_xlabel('')
ax2.set_ylabel('FX level', labelpad=10)

plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.5)

# Save the figure
bench_nominprice_f = os.path.join(output_folder, 'benchmark_no_minprice.pdf')
plt.savefig(bench_nominprice_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/22b52b7e8a239d6590d164d400ed1c49d914c57f03eeced1c3dc820364fb600c.png

Figure 13. Conditional CDF and Discretionary FX Interventions#

# Local merge to have the same axis
dd = pd.merge(dmfxi['pit'], dfd['FXI cdf'],
              left_index=True, right_index=True, how='left')

fig, ax1 = plt.subplots(1,1)
# Level plot
ax1.plot(dd.index, 100*dd['pit'], lw=1)
ax1.scatter(dd.index, 100*dd['FXI cdf'], alpha=0.8, c='red', marker='o', s=200)
ax1.set_title('', y=1.02)
ax1.set_xlabel('')
ax1.set_ylabel('Percentiles', labelpad=10)

ax1.axhline(y=5, color='red', linestyle='--', lw=2)
ax1.axhline(y=95, color='red', linestyle='--', lw=2)
ax1.axhline(y=50, color='black', linestyle='-', lw=1)

# Add the ticks, if needed
ax1.set_yticks([]) # Remove the standard y ticks
new_t_l = [5, 95]
new_ticks_l = sorted(list(ax1.get_yticks()) + new_t_l)
extra_idx_l = [new_ticks_l.index(x) for x in new_t_l]
ax1.set_yticks(new_ticks_l) # Add new ticks

ax1.set_ylim(-5, 105)

# Save the figure
bench_no_minprice_cdf_f = os.path.join(output_folder, 'benchmark_no_minprice_cdf.pdf')
plt.savefig(bench_no_minprice_cdf_f, bbox_inches='tight')
plt.show()
plt.close('all')
../../_images/1b14701548adbcee50369ad0cbf84305c96d43a64023ac97ba80c8486fde70c9.png

Optimize Model#

dgo  = dg.optimize()
mean_optim = dgo.optimize_mean()
Optimizing the Mean model. step 1: exog_l
100%|██████████| 255/255 [00:21<00:00, 11.97it/s]
Best Out-Of-Sample combination of exogenous variables: 
Bid ask abs,Forward points first difference,EURUSD log returns,VIX first diff,Oil prices log returns,FX intervention dummy lag
Optimizing the Mean model. step 2: number of lags
100%|██████████| 10/10 [00:00<00:00, 11.14it/s]
Best Out-Of-Sample number of lags: 0
vol_dist_optimization = dgo.optimize_vol_distrib()
vol_dist_optimization
100%|██████████| 28/28 [03:17<00:00,  7.04s/it]
Best Out-Of-Sample Volatility Model: RiskMetric
Best Out-Of-Sample Distribution Family: SkewStudent

KS_normalized_innovations KS_normalized_innovations_pvalues KS_PIT_test KS_PIT_test_pvalues Rossi_Shekopysan_PIT_test_tails ... Rossi_Shekopysan_PIT_test_right_tail log_score tails_log_score left_tail_log_score right_tail_log_score
volatility_model distribution
Constant Normal False 0.0 False 0.0 False ... False -5.04257 -1.566547 -1.08566 -1.086617
ARCH Normal False 0.0 False 0.0 False ... False -5.009953 -1.459077 -1.099263 -1.035228
RiskMetric Normal False 0.000547 False 0.000547 False ... False -4.986837 -1.467944 -1.080434 -1.054001
GARCH Normal False 0.000742 False 0.000742 False ... False -4.980809 -1.470709 -1.090697 -1.048915
GJR-GARCH Normal False 0.005783 False 0.005783 False ... False -4.974227 -1.4876 -1.120022 -1.045556
EGARCH Normal False 0.000092 False 0.000092 False ... False -4.97205 -1.485041 -1.122117 -1.034156
EWMA Normal False 0.024125 False 0.024125 False ... False -4.95291 -1.506733 -1.110205 -1.078375
RiskMetric StudentT False 0.010851 False 0.010851 False ... False -4.879082 -1.604971 -1.190337 -1.133393
GJR-GARCH SkewStudent False 0.039789 False 0.039789 False ... False -4.875905 -1.62644 -1.191692 -1.161776
EGARCH SkewStudent False 0.026603 False 0.026603 False ... False -4.873035 -1.629643 -1.199987 -1.155391
GARCH StudentT False 0.012535 False 0.012535 False ... False -4.867998 -1.621192 -1.208556 -1.137135
GJR-GARCH StudentT False 0.011123 False 0.011123 False ... False -4.863588 -1.63355 -1.229413 -1.135201
EGARCH StudentT False 0.008192 False 0.008192 False ... False -4.861173 -1.631491 -1.229093 -1.128699
EWMA StudentT False 0.004003 False 0.004003 False ... False -4.856825 -1.639105 -1.215933 -1.155787
RiskMetric GeneralizedError False 0.000802 False 0.000802 False ... False -4.804087 -1.632333 -1.214081 -1.14706
ARCH SkewStudent False 0.0 False 0.0 False ... False -4.801348 -1.741063 -1.264074 -1.26724
GARCH GeneralizedError False 0.002497 False 0.002497 False ... False -4.796392 -1.640416 -1.22777 -1.145731
ARCH StudentT False 0.0 False 0.0 False ... False -4.793713 -1.743412 -1.307338 -1.226357
GJR-GARCH GeneralizedError False 0.002383 False 0.002383 False ... False -4.793194 -1.652802 -1.245893 -1.146811
EGARCH GeneralizedError False 0.000739 False 0.000739 False ... False -4.791681 -1.649599 -1.24606 -1.138265
EWMA GeneralizedError False 0.000058 False 0.000058 False ... False -4.773021 -1.670509 -1.243921 -1.172531
Constant StudentT False 0.02714 False 0.02714 False ... False -4.76081 -1.840813 -1.32814 -1.263373
ARCH GeneralizedError False 0.0 False 0.0 False ... False -4.651161 -1.798946 -1.345029 -1.263181
Constant GeneralizedError False 0.00069 False 0.00069 False ... False -4.58129 -1.877664 -1.350475 -1.290213
RiskMetric SkewStudent True 0.175627 True 0.175627 True ... False -4.891099 -1.596891 -1.15392 -1.15908
GARCH SkewStudent True 0.207382 True 0.207382 True ... False -4.880204 -1.611642 -1.170178 -1.162337
EWMA SkewStudent True 0.154791 True 0.154791 True ... False -4.867917 -1.629429 -1.177134 -1.180854
Constant SkewStudent True 0.954117 True 0.954117 True ... True -4.764402 -1.837485 -1.306421 -1.27976

28 rows × 11 columns

params_finetuning = dgo.fine_tune_model()
params_finetuning
Fine Tune the best model
exog_l = ['Bid ask abs', 'Forward points first difference', 'EURUSD log returns', 'VIX first diff', 'Oil prices log returns', 'FX intervention dummy lag']
lags = 0
volatility model = RiskMetric
distribution family = SkewStudent
Stabilize Parameters with ZigZag method ...
Converged !
iteration_0 iteration_1 iteration_2 iteration_3
params pvalues params pvalues params pvalues params pvalues
Const -1.216365 2.309328e-01 -0.607584 4.481975e-01 -0.608573 4.475783e-01 -0.607069 4.486395e-01
Bid ask abs 179.577584 1.419435e-03 130.946085 2.341489e-03 133.040555 2.061630e-03 133.283738 2.026134e-03
Forward points first difference 29.503987 4.509421e-11 25.199999 2.574928e-24 24.954502 1.246611e-23 24.939480 1.451601e-23
EURUSD log returns -0.216986 6.049060e-35 -0.160931 1.556255e-30 -0.152832 6.030364e-28 -0.151594 1.419340e-27
VIX first diff 16.435769 2.246135e-62 16.174239 2.540096e-133 16.057678 1.592706e-135 16.032580 5.843260e-136
Oil prices log returns -0.021753 9.899393e-06 -0.022793 2.262161e-11 -0.022642 2.016052e-11 -0.022590 2.084256e-11
FX intervention dummy lag -7.435743 1.756491e-01 2.039140 6.738948e-01 1.754372 7.209052e-01 1.741334 7.228677e-01
eta 8.396661 8.149240e-27 8.324480 2.356683e-27 8.332597 2.653405e-27 8.334466 2.729595e-27
lambda 0.097658 1.369063e-07 0.101543 7.398261e-08 0.102415 5.931720e-08 0.102566 5.474931e-08
final_forecaster = dgo.final_model.forecast()
final_forecaster.plot.plot_pdf_rule()
Model is fixed and won't be fitted again. The parameter provided will be used
                           AR-X - RiskMetrics2006 Model Results                          
=========================================================================================
Dep. Variable:                    FX log returns   R-squared:                          --
Mean Model:                                 AR-X   Adj. R-squared:                     --
Vol Model:                       RiskMetrics2006   Log-Likelihood:               -30537.2
Distribution:      Standardized Skew Student's t   AIC:                           61092.4
Method:                User-specified Parameters   BIC:                           61152.2
                                                   No. Observations:                 5681
Date:                           Sat, Apr 15 2023                                         
Time:                                   10:18:55                                         
                Mean Model                
==========================================
                                      coef
------------------------------------------
Const                              -0.6071
Bid ask abs                       133.2837
Forward points first difference    24.9395
EURUSD log returns                 -0.1516
VIX first diff                     16.0326
Oil prices log returns             -0.0226
FX intervention dummy lag           1.7413
     Distribution    
=====================
                 coef
---------------------
eta            8.3345
lambda         0.1026
=====================

Results generated with user-specified parameters.
Std. errors not available when the model is not estimated, 
<Axes: title={'center': 'Conditional density and intervention rule based on 2020-10-14 information'}, xlabel='bps', ylabel='density'>
../../_images/5ba5cba177900762d7807f12500fe3fe70d3d08cfad44c8eb7f4a9b4c7188e5b.png