CNH to USD pair represents the offshore Chinese Yuan against US Dollar, CNH uses the letters CNY when trading inside of China. The Yuan used to be pegged to the US Dollar but is now allowed to trade a limited distance against the reserve currency on a daily basis. China has used its control over its exchange rate to help ward off global financial crisis.
The Standard & Poor’s 500, often abbreviated as the S&P 500, or just the S&P, is an American stock market index based on the market capitalizations of 500 large companies having common stock listed on the NYSE or NASDAQ.
This project is to investigate the relationship between CNH to USD exchange rate and S&P 500 index, and furthermore, whether US currency rate on emerging market relys on US equity market performance. If any proven relationship is discovered or confirmed, it can be a used as an effective proxy for exchange rate prediction. It can also make a significant impact on macroeconomic decisions of central banks, hedging and risk management for investors as well as any cross-border business that requires more than one currency for clearing and settlement purpose.
In this project, we look at the CNH to USD exchange rate and S&P 500 index in 2017 (251 trading days). For the convenience of the data processing, two data sets have been combined into one .csv file and here is a quick look at the data sets after we read them. The historical data is downloaded from Investing.com \(^{[2]}\) and Yahoo finance \(^{[3]}\). A time plot for both time series may give a general overview of their behavior.
data = read.csv(file="C:/Users/hwycl/OneDrive/Course 2017-2018 Winter/STATS531/mid project/midtermdata.csv", header = TRUE)
head(data)
## ï..Date SP500 CurPrice
## 1 1/3/2017 2257.83 0.1436967
## 2 1/4/2017 2270.75 0.1456346
## 3 1/5/2017 2269.00 0.1473123
## 4 1/6/2017 2276.98 0.1459811
## 5 1/9/2017 2268.90 0.1453488
## 6 1/10/2017 2268.90 0.1446990
c = data$CurPrice
s = data$SP500
plot(ts(cbind(c,s)),main = "CNH to USD Exchange Rate and S&P 500 Index in 2017",xlab="Day")
plot(ts(cbind(log(c),log(s))),main = "CNH to USD Exchange Rate and S&P 500 Index in Logrithm in 2017",xlab="Day")
We would like to check whether there is any common monthly or quaterly seasonality in two time series. Let’s plot the smoothed periodogram for both time series first. It seems that there is no obvious cycles in neither CNHUSD exchange rate nor S&P500 index in 2017. After taking first order difference, we observed different frequency patterns in both time series.
spectrum(c,spans=c(3,5,3),main="CNH to USD Exchange Specturm")
spectrum(s,spans=c(3,5,3),main="S&P 500 Exchange Specturm")
The frequency peaks for two time series do no overlap after taking first order difference
spectrum(diff(c),spans=c(3,5,3),main="First Order Difference CNH to USD Exchange Specturm")
spectrum(diff(s),spans=c(3,5,3),main="First Order Difference S&P 500 Exchange Specturm")
Both time series as increasing in the past 2017, there might be underlying relationship between these two time series. However, we need to de-trend them first.
dc=diff(c)
ds=diff(s)
plot(ts(cbind(dc,ds)),main = "Daily Difference on CNH to USD Exchange Rate and S&P 500 Index",xlab="Day")
We also would like to see the correlation function for first order difference of these two time series. Neither of them has obvious autocorrelation, however, ACF values follow a similar pattern as lags grows.
acf(dc,main="ACF of Daily Difference on CNH to USD Exchange Rate")
acf(ds,main="ACF of Daily Difference on S&P 500 Index")
Let’s conduct AIC analysis for first order difference of CNH USD Exchange rate, ARMA(4,4) is the may be the best fit. That is to say, we adopt ARIMA(4,1,4) for original time series.
aic_table <- function(data,P,Q){
table <- matrix(NA,(P+1),(Q+1))
for(p in 0:P) {
for(q in 0:Q) {
table[p+1,q+1] <- arima(data,order=c(p,0,q))$aic
}
}
dimnames(table) <- list(paste("<b> AR",0:P, "</b>", sep=""),paste("MA",0:Q,sep=""))
table
}
dc_aic_table <- aic_table(dc,5,5)
require(knitr)
kable(dc_aic_table,digits=2)
MA0 | MA1 | MA2 | MA3 | MA4 | MA5 | |
---|---|---|---|---|---|---|
AR0 | -3200.71 | -3201.65 | -3202.20 | -3200.55 | -3198.75 | -3196.75 |
AR1 | -3201.04 | -3201.04 | -3200.66 | -3198.66 | -3196.75 | -3194.77 |
AR2 | -3202.31 | -3200.62 | -3198.66 | -3196.67 | -3202.01 | -3200.26 |
AR3 | -3200.47 | -3198.63 | -3196.64 | -3198.65 | -3196.91 | -3195.15 |
AR4 | -3198.93 | -3196.93 | -3202.31 | -3198.08 | -3202.55 | -3200.55 |
AR5 | -3196.93 | -3194.93 | -3195.82 | -3195.39 | -3200.55 | -3198.55 |
After fitting the data to ARMA(4,4), we can test the residual as follows. The \(/sigma^2\) for error term is pretty small and there is no obvious evidence against normality of error terms.
armadc=arima(x=dc,order = c(4,0,4))
armadc
##
## Call:
## arima(x = dc, order = c(4, 0, 4))
##
## Coefficients:
## ar1 ar2 ar3 ar4 ma1 ma2 ma3 ma4
## -0.7985 -0.0842 -0.7534 -0.8555 0.9331 0.0533 0.6328 0.8405
## s.e. 0.1085 0.1709 0.1363 0.0765 0.1345 0.2331 0.1849 0.1012
## intercept
## 0e+00
## s.e. 1e-04
##
## sigma^2 estimated as 1.45e-07: log likelihood = 1611.27, aic = -3202.55
plot(armadc$residuals,type="p")
acf(armadc$residuals)
qqnorm(armadc$residuals)
qqline(armadc$residuals, col = 2)
To get start, we tried to fit the model using linear regression. Furthermore, the residuals do not follow the normal distribution, we need to think about fitting a regression on ARMA error model.
l=lm(c~s)
summary(l)
##
## Call:
## lm(formula = c ~ s)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.0025552 -0.0009577 -0.0004101 0.0008333 0.0056721
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.862e-02 2.191e-03 45.00 <2e-16 ***
## s 2.024e-05 8.939e-07 22.65 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.001546 on 249 degrees of freedom
## Multiple R-squared: 0.6732, Adjusted R-squared: 0.6719
## F-statistic: 512.9 on 1 and 249 DF, p-value: < 2.2e-16
plot(l$residuals)
acf(l$residuals)
qqnorm(l$residuals)
qqline(l$residuals, col = 2)
Let’s conduct AIC analysis for residuals, ARMA(5,4) is the may be the best fit for the residuals.
aic_table <- function(data,P,Q){
table <- matrix(NA,(P+1),(Q+1))
for(p in 0:P) {
for(q in 0:Q) {
table[p+1,q+1] <- arima(data,order=c(p,0,q))$aic
}
}
dimnames(table) <- list(paste("<b> AR",0:P, "</b>", sep=""),paste("MA",0:Q,sep=""))
table
}
residual_l_aic_table <- aic_table(l$residuals,5,5)
require(knitr)
kable(residual_l_aic_table,digits=2)
MA0 | MA1 | MA2 | MA3 | MA4 | MA5 | |
---|---|---|---|---|---|---|
AR0 | -2534.54 | -2784.73 | -2923.12 | -2990.52 | -3033.45 | -3058.35 |
AR1 | -3131.98 | -3130.50 | -3129.05 | -3128.28 | -3126.37 | -3124.67 |
AR2 | -3130.45 | -3128.63 | -3127.71 | -3126.31 | -3124.28 | -3124.35 |
AR3 | -3129.15 | -3127.98 | -3125.00 | -3131.35 | -3122.82 | -3122.36 |
AR4 | -3128.55 | -3126.69 | -3131.35 | -3130.29 | -3127.66 | -3125.82 |
AR5 | -3126.85 | -3125.14 | -3123.15 | -3127.58 | -3131.58 | -3129.57 |
Theoretically we can do better than linear regression, and this can be done by fitting a regression with ARMA(5,4) error model, as follows. We see a lower \(\sigma^2\) for error terms. However, I actually don’t think it improves that much. \(AIC=-3206.28\), almost the same as the previous model 4.3.1 where \(AIC = -3202.55\). Moreover, the coefficient for S&P 500 index \(s\) is zero, which also indicates that it does not really helps predicting CNH USD exchange rate.
armalr=arima(x=c,order = c(5,0,4),xreg=s)
armalr
##
## Call:
## arima(x = c, order = c(5, 0, 4), xreg = s)
##
## Coefficients:
## ar1 ar2 ar3 ar4 ar5 ma1 ma2 ma3
## 0.2049 0.7213 -0.6900 -0.0978 0.8475 0.9319 0.0470 0.6379
## s.e. 0.1131 0.0998 0.1357 0.1021 0.0781 0.1407 0.2391 0.1914
## ma4 intercept s
## 0.8441 0.1550 0
## s.e. 0.1076 0.0026 0
##
## sigma^2 estimated as 1.453e-07: log likelihood = 1615.14, aic = -3206.28
Here we basically follow the same procedure as 4.3.2, except we use first order difference rather than original time series, we start with linear regression. Apparently, noted that the coefficient for S&P 500 index \(ds\) is pretty small. Furthermore, the residuals do not follow the normal distribution, we need to think about fitting a regression on ARMA error model.
lr=lm(dc~ds)
summary(lr)
##
## Call:
## lm(formula = dc ~ ds)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.340e-03 -2.101e-04 -4.056e-05 1.821e-04 1.953e-03
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.730e-05 2.543e-05 1.860 0.064 .
## ds -4.827e-06 2.462e-06 -1.961 0.051 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.0003968 on 248 degrees of freedom
## Multiple R-squared: 0.01527, Adjusted R-squared: 0.0113
## F-statistic: 3.845 on 1 and 248 DF, p-value: 0.051
plot(lr$residuals)
acf(lr$residuals)
qqnorm(lr$residuals)
qqline(lr$residuals, col = 2)
Let’s conduct AIC analysis for residuals, ARMA(2,4) is the may be the best fit for the residuals.
aic_table <- function(data,P,Q){
table <- matrix(NA,(P+1),(Q+1))
for(p in 0:P) {
for(q in 0:Q) {
table[p+1,q+1] <- arima(data,order=c(p,0,q))$aic
}
}
dimnames(table) <- list(paste("<b> AR",0:P, "</b>", sep=""),paste("MA",0:Q,sep=""))
table
}
residual_aic_table <- aic_table(lr$residuals,5,5)
require(knitr)
kable(residual_aic_table,digits=2)
MA0 | MA1 | MA2 | MA3 | MA4 | MA5 | |
---|---|---|---|---|---|---|
AR0 | -3204.56 | -3206.04 | -3207.15 | -3205.25 | -3203.39 | -3201.41 |
AR1 | -3205.24 | -3205.96 | -3205.30 | -3203.31 | -3201.39 | -3199.39 |
AR2 | -3207.03 | -3205.03 | -3203.34 | -3201.34 | -3208.35 | -3206.44 |
AR3 | -3205.03 | -3203.09 | -3204.88 | -3204.90 | -3202.94 | -3199.24 |
AR4 | -3203.53 | -3201.53 | -3199.16 | -3202.95 | -3205.01 | -3205.15 |
AR5 | -3201.53 | -3199.53 | -3206.70 | -3201.04 | -3200.92 | -3203.12 |
Theoretically we can do better than linear regression, and this can be done by fitting a regression with ARMA(2,4) error model, as follows. We see a lower \(\sigma^2\) for error terms. However, I actually don’t think it improves that much. \(AIC=-3206.53\), almost the same as the previous model 4.3.1 where \(AIC = -3202.55\). Moreover, the coefficient for S&P 500 index \(ds\) is zero, which also indicates that it does not really helps predicting CNH USD exchange rate.
armalr=arima(x=dc,order = c(2,0,4),xreg=ds)
armalr
##
## Call:
## arima(x = dc, order = c(2, 0, 4), xreg = ds)
##
## Coefficients:
## ar1 ar2 ma1 ma2 ma3 ma4 intercept ds
## 0.4738 -0.8991 -0.3717 0.7898 0.1485 -0.1796 0e+00 0
## s.e. 0.0434 0.0200 0.0728 0.0642 0.0670 0.0641 1e-04 NaN
##
## sigma^2 estimated as 1.439e-07: log likelihood = 1612.26, aic = -3206.53
We decided to adopt ARIMA(4,1,4) to model 2017 CNH USD exchange rate. Let’s simulation a ARIMA(4,1,4) process and compare it with origianl time series. We can say that the specturm captured some important information of data set. As a time series, CNH USD itself is a decent predictor if ARIMA(4,1,4) is as prediction model.
simuc=arima.sim(n=251,list(order=c(4,1,4),ar=armadc$coef[1:4],ma=armadc$coef[5:8]))
spectrum(simuc,spans=c(3,5,3),main="Simulated CNH to USD Exchange Specturm")
After the analysis above, there is no strong evidence to claim S&P 500 index can greatly help predicting CNH USD exchange rate, based on 2017 data. It might have several reasons:
We can add other predicting factors to our analysis, or extend time window to include more data points, or conduct nonlinear variable transformation if we plan to further imporve our analysis.
The data analysis report is very intuitive and easy to follow. We first conducted data overview for two time series respectively, and tried to find out common pattern is frequency. Then we estimate two models, with or without S&P 500 index. By comparing coefficients value, AIC and \(\sigma^2\) for error terms, we reached our conclusion.
[1] Edward L. Ionides, STATS 531 Class notes 4,5,6,7
[2] CNH to USD Exchange Rate, https://www.investing.com/currencies/usd-cnh-historical-data
[3] S&P 500 Index, https://finance.yahoo.com/quote/%5EGSPC/history?p=%5EGSPC
[4] R.H.Shmway and D.S.Stoffer,Time Series Analysis and Its Application, Chapter 4