Investigating the Dynamics between Price-to-Rent Ratio and Market Fundamentals: A Vector Error Correction Model (VECM) Analysis in R

Introduction

The real estate market plays a vital role in the economic landscape of any country, acting as both a key indicator of economic health and a crucial component of household wealth. Among the numerous metrics used to gauge the state of the real estate market, the Price-to-Rent Ratio stands out as a valuable and widely recognized measure. It serves as a critical indicator of the affordability and investment attractiveness of residential properties, thus capturing the essence of the housing market dynamics.

In recent years, the global economy has experienced significant fluctuations, marked by economic crises, recessions, and periods of growth. The real estate sector, being highly sensitive to economic changes, has often been at the forefront of these movements. Understanding the factors that influence the Price-to-Rent Ratio is, therefore, of paramount importance for policymakers, investors, and market analysts seeking to make informed decisions in an ever-changing economic environment.

The primary objective of this study is to investigate the relationships and dynamics between the Price-to-Rent Ratio and a set of key macroeconomic variables. These variables include the Unemployment Rate, Stock Index, Debt-to-Income Ratio, Disposable Income, and Population. By employing the Vector Error Correction Model (VECM) framework, we aim to establish both short-term and long-term interactions among these variables, providing valuable insights into the factors shaping the Price-to-Rent Ratio.

Research Objectives:

To examine the short-term and long-term relationships between the Price-to-Rent Ratio and the selected macroeconomic variables

Methodology

To achieve our research objectives, we will utilize the Vector Error Correction Model (VECM) methodology. The VECM framework allows us to analyze both short-term deviations from equilibrium relationships and long-term trends, capturing the dynamic adjustments among the Price-to-Rent Ratio and the macroeconomic variables over time.

#loading the required packages
library(tsDyn)
library(vars)
library(urca)
library(readxl)
library(tidyverse)
library(tseries)

Loading the Data

maindat<-read_excel('vecmdata.xlsx')
glimpse(maindat)
## Rows: 84
## Columns: 10
## $ Date                                 <dttm> 2002-01-01, 2002-04-01, 2002-07-…
## $ `Houseprice index`                   <dbl> NA, NA, NA, NA, 100.00000, 96.596…
## $ `Rental index`                       <dbl> NA, NA, NA, NA, 100.0000, 110.209…
## $ `Unemployment rate`                  <dbl> 3.533333, 3.766667, NA, 4.200000,…
## $ `Real interest Rate Fisher Equation` <dbl> NA, NA, NA, NA, NA, 0.06500000, 0…
## $ Population                           <dbl> 512589, 514541, 515425, 516467, 5…
## $ `Household debt`                     <dbl> 894141.7, 919077.3, 942992.3, 966…
## $ `Disp income`                        <dbl> 161130, 172009, 174446, 175389, 1…
## $ `stock index`                        <dbl> 179.060, 148.450, 110.620, 115.21…
## $ Expectation                          <dbl> 19.54262, 19.13242, 10.92876, 2.1…

Focusing on data between 2004 and 2022

maindat<-maindat[maindat$Date >= "2004-01-01" &
maindat$Date <= "2022-10-01", ]

Data Preparation

## Renaming the variables
maindat<-rename(maindat,house_price_index=`Houseprice index`, rental_index=`Rental index`, unemployment_rate=`Unemployment rate`, house_debt= `Household debt`, disp_income=`Disp income`, stock_index=`stock index`)
## Obtaining the Price to rent ratio using  House price and rental index 
maindat1<-maindat%>%mutate(price_to_rent=house_price_index/rental_index)
#Checking missing values0
colSums(is.na(maindat1))
##                               Date                  house_price_index 
##                                  0                                  0 
##                       rental_index                  unemployment_rate 
##                                  0                                  0 
## Real interest Rate Fisher Equation                         Population 
##                                  0                                  0 
##                         house_debt                        disp_income 
##                                  0                                  0 
##                        stock_index                        Expectation 
##                                  0                                  0 
##                      price_to_rent 
##                                  0

No missing values in the data set

Testing for Stationarity using ADF Test

adf.test(diff(maindat1$house_price_index))
## Warning in adf.test(diff(maindat1$house_price_index)): p-value smaller than
## printed p-value
## 
## 	Augmented Dickey-Fuller Test
## 
## data:  diff(maindat1$house_price_index)
## Dickey-Fuller = -4.8143, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary
adf.test(diff(maindat1$unemployment_rate))
## 
## 	Augmented Dickey-Fuller Test
## 
## data:  diff(maindat1$unemployment_rate)
## Dickey-Fuller = -3.805, Lag order = 4, p-value = 0.02332
## alternative hypothesis: stationary
adf.test(diff(maindat1$stock_index))
## 
## 	Augmented Dickey-Fuller Test
## 
## data:  diff(maindat1$stock_index)
## Dickey-Fuller = -3.6379, Lag order = 4, p-value = 0.03617
## alternative hypothesis: stationary
adf.test(diff(maindat1$Population))
## 
## 	Augmented Dickey-Fuller Test
## 
## data:  diff(maindat1$Population)
## Dickey-Fuller = -3.6871, Lag order = 4, p-value = 0.03194
## alternative hypothesis: stationary
adf.test(diff(maindat1$disp_income))
## Warning in adf.test(diff(maindat1$disp_income)): p-value smaller than printed
## p-value
## 
## 	Augmented Dickey-Fuller Test
## 
## data:  diff(maindat1$disp_income)
## Dickey-Fuller = -5.1509, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary

At 5% level of significance, all the variables are statistically significant at the first difference. To run the Johansen Co-integration test, we need to find the appropriate lag. Let’s run the lag selection criteria.

maindat2<-maindat1%>%select(house_price_index, unemployment_rate, disp_income, stock_index, Population)
lagselection<- VARselect(maindat2, lag.max = 5, type = "const")
lagselection$selection
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##      4      3      3      4

Using the lag order selection criterion, The AIC and FPE statistics indicates 4 lags. We weil be using 4 lags for the Johansen Cointegration Test.

Johansen Co-integration Test

jtest<-ca.jo(maindat2, type = "trace", ecdet = "const", K = 4)
summary(jtest)
## 
## ###################### 
## # Johansen-Procedure # 
## ###################### 
## 
## Test type: trace statistic , without linear trend and constant in cointegration 
## 
## Eigenvalues (lambda):
## [1] 5.562366e-01 3.559108e-01 2.396547e-01 1.275879e-01 8.823309e-02
## [6] 4.521574e-16
## 
## Values of teststatistic and critical values of test:
## 
##            test 10pct  5pct  1pct
## r <= 4 |   6.56  7.52  9.24 12.97
## r <= 3 |  16.25 17.85 19.96 24.60
## r <= 2 |  35.70 32.00 34.91 41.07
## r <= 1 |  66.94 49.65 53.12 60.16
## r = 0  | 124.62 71.86 76.07 84.45
## 
## Eigenvectors, normalised to first column:
## (These are the cointegration relations)
## 
##                      house_price_index.l4 unemployment_rate.l4 disp_income.l4
## house_price_index.l4          1.000000000         1.000000e+00   1.000000e+00
## unemployment_rate.l4        -11.006446080         1.100535e+01  -1.468749e+01
## disp_income.l4                0.001807349        -4.380915e-04  -7.834441e-04
## stock_index.l4               -0.085321885        -1.537148e-01  -1.668333e-01
## Population.l4                -0.002939197        -7.576155e-05  -2.535996e-04
## constant                    967.699674445        -2.390015e+01   3.365177e+02
##                      stock_index.l4 Population.l4      constant
## house_price_index.l4   1.000000e+00  1.000000e+00  1.000000e+00
## unemployment_rate.l4   5.215113e+01 -1.286784e+01 -8.884744e+00
## disp_income.l4        -9.953815e-03 -1.320551e-03  3.158971e-03
## stock_index.l4         8.325769e-01 -8.366555e-02 -8.143422e-02
## Population.l4          7.627003e-03  8.250524e-04 -4.844966e-03
## constant              -2.705255e+03 -2.275530e+02  1.921081e+03
## 
## Weights W:
## (This is the loading matrix)
## 
##                     house_price_index.l4 unemployment_rate.l4 disp_income.l4
## house_price_index.d        -5.018237e-02          0.012981589   -0.045067342
## unemployment_rate.d         6.412331e-03         -0.004402782   -0.002814955
## disp_income.d              -1.022313e+02        -19.569791287  -82.308859820
## stock_index.d              -1.090375e+00          1.594152991    0.292996773
## Population.d               -6.067787e+00        -38.835015426    5.554261826
##                     stock_index.l4 Population.l4      constant
## house_price_index.d   -0.011233613  -0.037179521  4.826062e-15
## unemployment_rate.d   -0.001984202   0.004909443 -5.660411e-15
## disp_income.d         28.825908626  77.439408104  1.223346e-10
## stock_index.d         -0.048826350   0.017932925  5.506764e-13
## Population.d          -1.134438807  -0.495769414  2.236135e-12

Based on the test statistics above, the test statistics are greater than the critical values from rank 0 down to rank 2 at 5% level of significance. At rank 3, the test statistics is less than the critical value at 5%, hence we can not reject the null hypothesis and conclude that there are 3 cointegrating relationships.

Let’s implement the vector error correction model using the 3 cointegrating relationships.

Vector Error Correction Model

vecm_model<- VECM(maindat2, lag = 4, r =3, estim = "ML")
summary(vecm_model)
## #############
## ###Model VECM 
## #############
## Full sample size: 75 	End sample size: 70
## Number of variables: 5 	Number of estimated slope parameters 120
## AIC 2980.128 	BIC 3263.438 	SSR 5363493003
## Cointegrating vector (estimated by ML):
##    house_price_index unemployment_rate   disp_income   stock_index
## r1      1.000000e+00                 0 -4.065758e-20  -0.131176692
## r2      3.469447e-18                 1  1.355253e-20  -0.002155525
## r3     -2.842171e-14                 0  1.000000e+00 -15.704630857
##       Population
## r1 -0.0007440166
## r2  0.0000172500
## r3 -0.9876070419
## 
## 
##                            ECT1                    ECT2                    
## Equation house_price_index -0.1928(0.0829)*        1.5655(1.0191)          
## Equation unemployment_rate 0.0095(0.0107)          -0.3510(0.1319)*        
## Equation disp_income       -124.3107(194.7610)     1344.2890(2393.5420)    
## Equation stock_index       0.4347(0.7611)          30.3146(9.3541)**       
## Equation Population        -32.5666(14.6560)*      -498.0438(180.1171)**   
##                            ECT3                 Intercept                   
## Equation house_price_index  7.9e-05(0.0001)     -47.3468(46.1710)           
## Equation unemployment_rate  4.3e-05(1.4e-05)**  19.0203(5.9762)**           
## Equation disp_income       -0.1703(0.2539)      -93977.3307(108439.7957)    
## Equation stock_index       -0.0031(0.0010)**    -1090.1788(423.7876)*       
## Equation Population        0.0073(0.0191)       -64.5007(8160.2336)         
##                            house_price_index -1   unemployment_rate -1  
## Equation house_price_index 0.8095(0.1425)***      -2.9172(1.3525)*      
## Equation unemployment_rate -0.0084(0.0184)        0.4659(0.1751)*       
## Equation disp_income       166.3330(334.7641)     58.9107(3176.4600)    
## Equation stock_index       2.7900(1.3083)*        -29.8030(12.4137)*    
## Equation Population        24.4709(25.1914)       115.3074(239.0327)    
##                            disp_income -1       stock_index -1       
## Equation house_price_index -5.6e-05(0.0001)     -0.0213(0.0192)      
## Equation unemployment_rate -3.5e-05(1.4e-05)*   0.0029(0.0025)       
## Equation disp_income       -0.5990(0.2607)*     -41.4424(45.0234)    
## Equation stock_index       0.0031(0.0010)**     -0.6080(0.1760)**    
## Equation Population        -0.0037(0.0196)      -1.9782(3.3881)      
##                            Population -1       house_price_index -2 
## Equation house_price_index 0.0016(0.0010)      -0.3302(0.1942).     
## Equation unemployment_rate 0.0003(0.0001)*     -0.0273(0.0251)      
## Equation disp_income       -1.5502(2.3304)     22.6129(456.0237)    
## Equation stock_index       -0.0212(0.0091)*    -2.6848(1.7822)      
## Equation Population        -0.1215(0.1754)     -4.4252(34.3164)     
##                            unemployment_rate -2      disp_income -2      
## Equation house_price_index 0.7177(1.3813)            -0.0002(0.0001)*    
## Equation unemployment_rate -0.1105(0.1788)           -3.6e-05(1.5e-05)*  
## Equation disp_income       -3325.2422(3244.1765)     -0.3448(0.2690)     
## Equation stock_index       -5.2625(12.6784)          0.0015(0.0011)      
## Equation Population        144.4379(244.1284)        0.0201(0.0202)      
##                            stock_index -2        Population -2      
## Equation house_price_index -0.0490(0.0195)*      -0.0024(0.0010)*   
## Equation unemployment_rate 0.0076(0.0025)**      0.0001(0.0001)     
## Equation disp_income       -24.0556(45.7904)     -1.1799(2.2869)    
## Equation stock_index       -0.4074(0.1790)*      -0.0323(0.0089)*** 
## Equation Population        0.2739(3.4458)        0.4159(0.1721)*    
##                            house_price_index -3   unemployment_rate -3    
## Equation house_price_index 0.0391(0.1843)         -0.8537(1.2135)         
## Equation unemployment_rate 0.0095(0.0239)         0.4459(0.1571)**        
## Equation disp_income       145.9643(432.9081)     1666.2445(2850.1076)    
## Equation stock_index       -0.2048(1.6918)        -17.4037(11.1383)       
## Equation Population        37.8971(32.5769)       396.6538(214.4743).     
##                            disp_income -3        stock_index -3       
## Equation house_price_index -0.0002(0.0001)       -0.0163(0.0200)      
## Equation unemployment_rate -1.8e-05(1.5e-05)     0.0019(0.0026)       
## Equation disp_income       -0.4449(0.2650).      -12.4770(46.9112)    
## Equation stock_index       0.0003(0.0010)        -0.1114(0.1833)      
## Equation Population        0.0217(0.0199)        -0.6225(3.5301)      
##                            Population -3       house_price_index -4  
## Equation house_price_index -0.0017(0.0011)     0.2004(0.1556)        
## Equation unemployment_rate 0.0004(0.0001)**    -0.0008(0.0201)       
## Equation disp_income       -1.0152(2.5742)     171.2797(365.4477)    
## Equation stock_index       -0.0181(0.0101).    0.2208(1.4282)        
## Equation Population        -0.0702(0.1937)     -19.6083(27.5004)     
##                            unemployment_rate -4     disp_income -4       
## Equation house_price_index -0.6598(1.0958)          -8.9e-05(8.7e-05)    
## Equation unemployment_rate 0.1683(0.1418)            7.4e-06(1.1e-05)    
## Equation disp_income       -324.2258(2573.5963)     0.2145(0.2045)       
## Equation stock_index       -0.0973(10.0577)         -0.0004(0.0008)      
## Equation Population        42.7762(193.6664)        0.0006(0.0154)       
##                            stock_index -4       Population -4      
## Equation house_price_index -0.0305(0.0156).     0.0011(0.0011)     
## Equation unemployment_rate 0.0038(0.0020).      0.0002(0.0001)     
## Equation disp_income       32.0569(36.5970)     -0.8567(2.4860)    
## Equation stock_index       -0.2955(0.1430)*     0.0045(0.0097)     
## Equation Population        1.2572(2.7540)       0.1242(0.1871)

According to the ECTs, The Vector Error Correction Model results shows that there is a significant negative long term relationship between Price-to-Rent Ratio and unemployment_rate. Also there is a significant negative relationship between Price-to-Rent Ratio and stock index.

Model Diagnostic

Normality Test for the Residuals

modv<-vec2var(jtest, r=3)
res<-normality.test(modv, multivariate.only = T)
res
## $JB
## 
## 	JB-Test (multivariate)
## 
## data:  Residuals of VAR object modv
## Chi-squared = 266.79, df = 10, p-value < 2.2e-16
## 
## 
## $Skewness
## 
## 	Skewness only (multivariate)
## 
## data:  Residuals of VAR object modv
## Chi-squared = 44.961, df = 5, p-value = 1.478e-08
## 
## 
## $Kurtosis
## 
## 	Kurtosis only (multivariate)
## 
## data:  Residuals of VAR object modv
## Chi-squared = 221.83, df = 5, p-value < 2.2e-16

The residuals of the model are not normally distributed.

Hamid Abdulsalam
Hamid Abdulsalam
Data Scientist

Statistical inference, machine learning, deep learning, and its use in the financial and medical fields are some of my research interests.