Included this week:
The Central Bank of Ireland have “re-calibrated” mortgage lending rules.
The Statement can be found here
First Time Buyers
Second & Subsequent buyers (SSB)
Proportionate allowances
Additionally - From a “fresh start” perspective, borrowers who are divorced or separated or have undergone bankruptcy or insolvency may be considered FTBs for the mortgage measures (where they no longer have an interest in the previous property). - FTBs who get a top-up loan or re-mortgage with an increase in the principal may be considered FTBs, provided the property remains their primary home.
*The median house price relative to the prior cap can be seen in the “Characteristics of Household Purchases” Section”.
The CSO released data on the Characteristics of Residential Property Purchasers. The next two sections contains data based on this series.
The below illustrations are called “Sankey” charts, they are used to illustrate the flow from one object to another. Our objects are residences, so the county a purchaser moved from is on the left column and where they moved to is on the right. The colour of the flow is based on the destination county.
For the GDA I’ve displayed to sets of sankey charts, the latter I’ve removed Dublin as a destination county. As you can see the large number of moving within Dublin makes assessing the other counties more difficult.
#### Filter for From.Dublin & To.GDA
CRP05.F_DUB.T_GDA <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="GDA") %>%
filter(To.Region=="Dublin"|To.Region=="GDA")
options(scipen=999)
ggplot(CRP05.F_DUB.T_GDA,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The GDA")+scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
##### Version 2 Filter for From.Dublin & To.Midlands
CRP05.F_DUB.T_GDA2 <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="GDA") %>%
filter(To.Region=="GDA")
ggplot(CRP05.F_DUB.T_GDA2,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The GDA")+
labs(subtitle="Dublin excluded from destination county")+scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
#### Filter for From.Dublin & To.Midlands
CRP05.F_DUB.T_MID <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="Midlands") %>%
filter(To.Region=="Midlands")
ggplot(CRP05.F_DUB.T_MID,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The Midlands")+
labs(subtitle="Dublin excluded from destination county")+scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
#### Filter for From.Dublin & To.North
CRP05.F_DUB.T_NOR <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="North") %>%
filter(To.Region=="North")
ggplot(CRP05.F_DUB.T_NOR,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The North")+
labs(subtitle="Dublin excluded from destination county")+scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
#### Filter for From.Dublin & To.West
CRP05.F_DUB.T_WES <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="West") %>%
filter(To.Region=="West")
ggplot(CRP05.F_DUB.T_WES,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The West")+
labs(subtitle="Dublin excluded from destination county")+
scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
#### Filter for From.Dublin & To.South
CRP05.F_DUB.T_SOU <- CRP05 %>%
filter(From.Region=="Dublin"|From.Region=="South") %>%
filter(To.Region=="South")
ggplot(CRP05.F_DUB.T_SOU,
aes(y=value,
axis1=Purchasers.Location.Given.During.Purchase,,axis2=County.Where.Property.Was.Purchased))+
geom_alluvium(aes(fill=County.Where.Property.Was.Purchased),width = 1/12) +
geom_stratum(width = 1/12, fill = "black", color = "grey") +
geom_label(stat = "stratum", aes(label = after_stat(stratum)),size=2.5)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6))+
ggtitle("Residential Movement between Dublin & The South")+
labs(subtitle="Dublin excluded from destination county")+
scale_fill_brewer(type = "qual", palette = "Spectral")+
facet_wrap(~year,ncol=2)
Fig.Head.VOL <- CRP01.IRL.VOL.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#F59A08")+
geom_text_repel(aes(label=value),alpha=0.7,size=3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Volume of Sales")+
xlab(NULL)+
ylab("number")
Fig.Head.AGE <- CRP01.IRL.AGE.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#08F5BC")+
geom_text_repel(aes(label=value),alpha=0.7,size=3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Age")+
xlab(NULL)+
ylab("number")
Fig.Head.PRC <- CRP01.IRL.PRC.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#32AE04")+
geom_text_repel(aes(label=value),alpha=0.7,size=3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Price")+
xlab(NULL)+
ylab("euro")
Fig.Head.INC <- CRP01.IRL.INC.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#0416AE")+
geom_text_repel(aes(label=value),alpha=0.7,size=3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Income")+
labs(caption="CRP01: Characteristics of Residential Property Purchasers")+
xlab(NULL)+
ylab("euro")
Fig.Head <- Fig.Head.VOL + Fig.Head.AGE + Fig.Head.PRC + Fig.Head.INC
Fig.Head
By 2021 joint transactions were 1.5 times sole transactions, on average they were three years younger with a combined income that was 1.9 times higher than sole purchasers. They bought houses at 1.4 times at a median price of € 318 k, which is 13.5% above the aggregate median price.
####### View the 4 statistical measures for Ireland for each transaction category and aggregated buyers (i.e. OTH.ALL)
##### Title: Figure.IRL.TCAT
Fig.IRL.TCAT.VOL <- CRP01.IRL.VOL.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#F59A08")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Volume of Sales")+
xlab(NULL)+
ylab("number")
Fig.IRL.TCAT.AGE <- CRP01.IRL.AGE.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#08F5BC")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Age")+
xlab(NULL)+
ylab("number")
Fig.IRL.TCAT.PRC <- CRP01.IRL.PRC.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#32AE04")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Price")+
xlab(NULL)+
ylab("euro")
Fig.IRL.TCAT.INC <- CRP01.IRL.INC.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#0416AE")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Income")+
xlab(NULL)+
ylab("euro")
Fig.IRL.TCAT <- Fig.IRL.TCAT.VOL + Fig.IRL.TCAT.AGE + Fig.IRL.TCAT.PRC + Fig.IRL.TCAT.INC
Fig.IRL.TCAT
The Buyer Category, those with and without children, trend is largely explained by the same factors as the Transaction Category. The gap bewteen incomes and prices are narrower at 1.35 and 1.17 respectively.
####### View the 4 statistical measures for Ireland for aggregated transaction category and each buyer (i.e. ALL.OTH)
##### Title: Figure.IRL.TBUY || Filter >= 2016
CRP01.IRL.VOL.ALL.OTH <- CRP01.IRL.VOL.ALL.OTH %>%
filter(year>="2016")
Fig.IRL.TBUY.VOL <- CRP01.IRL.VOL.ALL.OTH %>%
ggplot(aes(x=year,y=value,group=Type.of.Buyer))+
geom_line(aes(colour=Type.of.Buyer),size=1.25,alpha=0.6)+
scale_colour_manual(values=c("#F59A08","#875501"))+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Volume of Sales")+
xlab(NULL)+
ylab("number")
CRP01.IRL.AGE.ALL.OTH <- CRP01.IRL.AGE.ALL.OTH %>%
filter(year>="2016")
Fig.IRL.TBUY.AGE <- CRP01.IRL.AGE.ALL.OTH %>%
ggplot(aes(x=year,y=value,group=Type.of.Buyer))+
geom_line(aes(colour=Type.of.Buyer),size=1.25,alpha=0.6)+
scale_colour_manual(values=c("#08F5BC","#06A07B"))+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Age")+
xlab(NULL)+
ylab("number")
CRP01.IRL.PRC.ALL.OTH <- CRP01.IRL.PRC.ALL.OTH %>%
filter(year>="2016")
Fig.IRL.TBUY.PRC <- CRP01.IRL.PRC.ALL.OTH %>%
ggplot(aes(x=year,y=value,group=Type.of.Buyer))+
geom_line(aes(colour=Type.of.Buyer),size=1.25,alpha=0.6)+
scale_colour_manual(values=c("#32AE04","#174D03"))+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Price")+
xlab(NULL)+
ylab("euro")
CRP01.IRL.INC.ALL.OTH <- CRP01.IRL.INC.ALL.OTH %>%
filter(year>="2016")
Fig.IRL.TBUY.INC <- CRP01.IRL.INC.ALL.OTH %>%
ggplot(aes(x=year,y=value,group=Type.of.Buyer))+
geom_line(aes(colour=Type.of.Buyer),size=1.25,alpha=0.6)+
scale_colour_manual(values=c("#0416AE","#030B53"))+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=6),
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Income")+
xlab(NULL)+
ylab("euro")
Fig.IRL.TBUY <- Fig.IRL.TBUY.VOL + Fig.IRL.TBUY.AGE + Fig.IRL.TBUY.PRC + Fig.IRL.TBUY.INC
Fig.IRL.TBUY
### NUTS 3 Region
#### Volume
CRP01.NUTS3.VOL.ALL.ALL <- CRP01.NUTS3.VOL.ALL %>%
filter(Type.of.Buyer=="All buyer types")
####### View the 4 statistical measures for NUTS3 across the aggregated categories (i.e. ALL.ALL)
CRP01.NUTS3.VOL.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#F59A08")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Volume of Sales")+
xlab(NULL)+
ylab("number")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.AGE.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#08F5BC")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Age")+
xlab(NULL)+
ylab("number")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.PRC.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#32AE04")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Price")+
xlab(NULL)+
ylab("euro")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.INC.ALL.ALL %>%
ggplot(aes(x=year,y=value,group=Statistic))+
geom_line(size=1.25,alpha=0.6,colour="#0416AE")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Income")+
xlab(NULL)+
ylab("euro")+
facet_wrap(~NUTS.3.Region,ncol=2)
# Fig.NUTS3 <- Fig.NUTS3.VOL + Fig.NUTS3.AGE + Fig.NUTS3.PRC + Fig.NUTS3.INC # May be unsuitable, run in Markdown and if so revert to individual
####### View the 4 statistical measures for NUTS3 for each transaction category and aggregated buyers (i.e. OTH.ALL)
##### Title: Figure.NUTS3.TCAT
CRP01.NUTS3.VOL.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#F59A08")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Volume of Sales")+
xlab(NULL)+
ylab("number")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.AGE.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#08F5BC")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Age")+
xlab(NULL)+
ylab("number")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.PRC.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#32AE04")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Price")+
xlab(NULL)+
ylab("euro")+
facet_wrap(~NUTS.3.Region,ncol=2)
CRP01.NUTS3.INC.OTH.ALL %>%
ggplot(aes(x=year,y=value,group=Transaction.Class))+
geom_line(aes(linetype=Transaction.Class),size=1.25,alpha=0.6,colour="#0416AE")+
geom_text_repel(aes(label=value),alpha=0.3,size=3,max.overlaps = 3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
ggtitle("Median Income")+
xlab(NULL)+
ylab("euro")+
facet_wrap(~NUTS.3.Region,ncol=2)
I’ve included variables for price to income, a loan ceiling/cap which is 3.5 times income plous 10% of that figure as a deposit and finally a the latter expressed as a percentage of the median price. - This being the spread between what the median buyer can afford vs what the average price is, in the absence of savings.
Naturally Central Bank rules are not the only factor that determines prices but the below gives us an indication of how far off on average the buyers are.
The median price exceeded the loan ceiling for the first rime in 2021, having acted as a cap in 2019 & 2020.
PTI.Facet1 <- RATIO %>%
ggplot(aes(x=year,y=PtI,group=NUTS.3.Region))+
geom_line(aes(y=CAPtI),alpha=0.8,linetype="dotted")+
geom_line(size=1.25,colour="#5DD7D2",alpha=0.7)+
geom_text_repel(aes(label=PtI),alpha=0.4)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
labs(caption="dotted line represents 3.5x median income plus 10% of that figure")+
ggtitle("Price to Income Ratio")+
ylab("price to income")
RATIO$price.lab <- comma((RATIO$price/1000),digits=0)
RATIO$CAP.lab <- comma((RATIO$CAP/1000),digits=0)
price.Facet2 <- RATIO %>%
ggplot(aes(x=year,y=price,group=NUTS.3.Region))+
geom_line(size=1.25,colour="#3C46A1",alpha=0.7)+
geom_line(aes(y=CAP),colour="darkred",alpha=0.7,size=1.25)+
geom_text_repel(aes(label=price.lab),colour="black",hjust="left",alpha=0.3)+
geom_text_repel(aes(label=CAP.lab,y=CAP),colour="darkred",hjust="left",vjust=0.5,alpha=0.3)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(size=12,face="bold"))+
labs(caption="red line represents 3.5x median income plus 10% of that figure")+
ggtitle("Median Prices vs Average Loan Capacity")
spr.colour <- ifelse(RATIO$SPR > 0,"darkred","#1b5545")
SPR.Facet3 <- RATIO %>%
ggplot(aes(x=year,y=SPR,group=NUTS.3.Region))+
geom_hline(yintercept = 0,alpha=0.6,linetype="dotted")+
geom_line(size=1.25,colour="#A13C56",alpha=0.7)+
geom_text_repel(aes(label=SPR,colour=spr.colour),hjust="left",alpha=0.5)+
scale_colour_manual(values=c("#CC0000","#1b5545"))+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(size=12,face="bold"))+
labs(caption="")+
ggtitle("Median Prices vs Average Loan Capacity: Spread")+
ylab("euro")
#### MAP
NUTS <- sf::read_sf("C:\\Users\\charten\\OneDrive - Glenveagh Properties\\Research & Development\\1. Analysis\\NUTS_RG_01M_2021_4326.shp")
NUTS.IE <- NUTS %>%
filter(CNTR_CODE=="IE")%>%
filter(LEVL_CODE=="3")
NUTS.CLEAN <- readxl::read_xlsx(path="C:\\Users\\charten\\OneDrive - Glenveagh Properties\\Research & Development\\1. Analysis\\NUTS3_CLEAN.xlsx")
RATIO.PRC_NUTS3 <- CRP01.NUTS3.PRC.ALL.ALL %>%
rename(price=value)
RATIO.PRC_NUTS3 <- RATIO.PRC_NUTS3 %>%
select(3:7)
RATIO.INC_NUTS3 <- CRP01.NUTS3.INC.ALL.ALL %>%
rename(income=value)
RATIO.INC_NUTS3 <- RATIO.INC_NUTS3 %>%
select(3:7)
RATIO_NUTS3 <- merge(RATIO.PRC_NUTS3,RATIO.INC_NUTS3,by=c("Transaction.Class","NUTS.3.Region","Type.of.Buyer","year"))
RATIO_NUTS3 <- RATIO_NUTS3 %>%
filter(year>="2012")
RATIO_NUTS3$PtI <- comma((RATIO_NUTS3$price/RATIO_NUTS3$income),digits = 2)
RATIO_NUTS3$CAP <- (RATIO_NUTS3$income*3.5)+(RATIO_NUTS3$income*3.5*.1)
RATIO_NUTS3$CAP <- comma(RATIO_NUTS3$CAP,big.mark = ",",digits=0)
RATIO_NUTS3$CAPtI <- comma((RATIO_NUTS3$CAP/RATIO_NUTS3$income),digits=2)
RATIO_NUTS3$SPR <- RATIO_NUTS3$CAP - RATIO_NUTS3$price
RATIO_NUTS3$SPR <- comma(RATIO_NUTS3$SPR,big.mark = ",",digits=0)
RATIO_NUTS3$SPRPC <- comma((RATIO_NUTS3$SPR/RATIO_NUTS3$price),big.mark = ",",digits=2)*100
RATIO_NUTS3 <- merge(RATIO_NUTS3,NUTS.CLEAN,by="NUTS.3.Region")
SHP.RATIO_NUTS3 <- merge(NUTS.IE,RATIO_NUTS3,by.x="NAME_LATN",by.y="NAME_LATN")
SHP.RATIO_NUTS3.21 <- SHP.RATIO_NUTS3 %>%
filter(year=="2021")
SPR.Facet4 <- SHP.RATIO_NUTS3.21 %>%
ggplot()+
geom_sf(aes(fill=SPRPC),alpha=0.6)+
theme_void()+
scale_fill_viridis_c(option = "magma", name="%")+
geom_sf_text(aes(label=SPRPC),color="navy",size=2.5)+
theme(legend.title = element_text(size = 10),
legend.text = element_text(size = 8))+
theme(legend.position = "right")+
theme(plot.title = element_text(size=12,face="bold"))+
facet_wrap(~year,ncol=4)+
theme(strip.text.x = element_text(size = 10,colour = "navy"))+
ggtitle("Spread as Percentage of price - NUTS 3 Regions")
#PTI.Facet1 + price.Facet2 + SPR.Facet3 + SPR.Facet4 + plot_layout(widths = c(1,1))
PTI.Facet1
price.Facet2
SPR.Facet3
SPR.Facet4
SHP.RATIO_NUTS3 %>%
ggplot()+
geom_sf(aes(fill=PtI),alpha=0.9)+
theme_void()+
scale_fill_distiller(palette = "Spectral", name="Price to Income")+
theme(legend.title = element_text(size = 10),
legend.text = element_text(size = 8))+
theme(legend.position = "left")+
theme(plot.title = element_text(size=16,face="bold"))+
facet_wrap(~year,ncol=3)+
theme(strip.text.x = element_text(size = 10,colour = "navy"))+
ggtitle("Price to Income - NUTS 3 Regions")
options(scipen=999)
RATIO_NUTS3_Max <- RATIO_NUTS3 %>%
group_by(NUTS.3.Region) %>%
slice(which.max(year))
RATIO_NUTS3_Max$lab <- paste("Spread : ", RATIO_NUTS3_Max$SPRPC," %")
spr.max.colour <- ifelse(RATIO_NUTS3_Max$SPR > 0,"darkred","#1b5545")
#
# # Option A
# RATIO_NUTS3 %>%
# ggplot(aes(x=year,y=price,group=NUTS.3.Region))+
# geom_line(size=1.25,colour="#3C46A1",alpha=0.7)+
# geom_line(aes(y=CAP),colour="darkred",alpha=0.7,size=1.25)+
# geom_text(data=RATIO_NUTS3_Max,aes(label=lab,y=105000,colour=spr.max.colour),hjust="right",alpha=0.6)+
# scale_colour_manual(values=c("#CC0000","#1b5545"))+
# theme_bw()+
# theme(legend.position = "none",
# plot.title = element_text(size=12,face="bold"))+
# labs(caption="red line represents 3.5x median income plus 10% of that figure")+
# ggtitle("Median Prices vs Average Loan Capacity")+
# facet_wrap(~NUTS.3.Region,ncol=2)
#
# # Option B
spr.colour <- ifelse(RATIO_NUTS3$SPR > 0,"darkred","#1b5545")
RATIO_NUTS3 %>%
ggplot(aes(x=year,y=SPRPC,group=NUTS.3.Region))+
geom_hline(yintercept = 0,alpha=0.4,linetype="dotted")+
geom_line(size=1.25,colour="#A13C56",alpha=0.7)+
geom_text_repel(aes(label=SPRPC,colour=spr.colour),alpha=0.7,size=2.75)+
scale_colour_manual(values=c("#CC0000","#1b5545"))+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(size=12,face="bold"))+
labs(caption="")+
ggtitle("Median Prices vs Average Loan Capacity: Spread %")+
ylab("%")+
facet_wrap(~NUTS.3.Region,ncol=2)
Included below are loan volumes and growth rates, interest rates and the proportion of mortgages which are variable rate loans in Ireland vs the Euro Area average.
Facet1 <- CBM01.OUT %>%
ggplot(aes(x=Date,y=value,group=Type.of.Loan,colour=Type.of.Loan))+
geom_line(size=1.5,alpha=0.5)+
geom_text_repel(aes(label=value),max.overlaps = 8,size=2.5)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(face="bold"))+
ggtitle("Outstanding Amount of Loans and Securitisations")+
labs(subtitle="CSO - CBM01: Loans, including Securitisations to Irish Residents")+
ylab("euro million")+
xlab(NULL)+
geom_text(data=CBM01.OUT_Max,aes(label=lab,x=Date,y=value,group=Type.of.Loan),size=3,hjust="right",vjust="bottom",fontface="bold",colour="black",alpha=0.6)
CBM01.GRY_Max <- CBM01.GRY %>%
group_by(Type.of.Loan) %>%
slice(which.max(Date))
CBM01.GRY_Max$lab <- paste(CBM01.GRY_Max$Month,":",CBM01.GRY_Max$value)
CBM01.GRY <- CBM01.GRY %>%
filter(Date >= "2004-01-01")
Facet2 <- CBM01.GRY %>%
ggplot(aes(x=Date,y=value,group=Type.of.Loan,colour=Type.of.Loan))+
geom_hline(yintercept = 0,alpha=0.5, linetype="dotted")+
geom_line(size=1.25,alpha=0.5)+
geom_text_repel(aes(label=value),max.overlaps = 10,size=2.5)+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(face="bold"))+
ggtitle("Annual Growth Rate")+
ylab("%")+
xlab(NULL)+
geom_text(data=CBM01.GRY_Max,aes(label=lab,x=Date,y=value,group=Type.of.Loan),size=3,hjust="right",vjust="bottom",fontface="bold",colour="black",alpha=0.6)+
facet_wrap(~Type.of.Loan,nrow=2,labeller = labeller(Type.of.Loan = label_wrap_gen(width = 45)))
Facet1 + Facet2 + plot_layout(ncol=2, widths = c(2,1))
We have been working of the ECB statistics for interest rate statistics, comparing them with other eurozone countries. Part of the CSOs obligations is to report statistics to Eurostat, the interest rate statistics that the CSO publishes from their database can be seen below. I’ve included a dotted line for the ECB statistic which is:
“Bank interest rates - loans to households for house purchase with a floating rate and an IRF period of up to one year (new business)”
CBM02 %>%
ggplot(aes(x=Date,y=value,group=Statistic,colour=Statistic))+
geom_line(size=1.25,alpha=0.6)+
scale_colour_manual(values = c("#0857F5","#F59A08"))+
geom_text_repel(aes(label=value),max.overlaps = 6,size=2.5)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(face="bold"))+
ggtitle("Retail Interest Rates: Households")+
labs(subtitle="CSO - CBM02",caption="Dotted line represents ECB statistic for Household Loans for Ireland")+
ylab("%")+
xlab(NULL)+
geom_text(data=CBM02_Max,aes(label=lab,x=Date,y=value,group=Statistic),size=3,hjust="right",vjust="bottom",fontface="bold",colour="black",alpha=0.8)+
geom_line(data=IE.BIR_H,aes(x=obstime,y=obsvalue,group=ref_area),colour="black",alpha=0.6,linetype="dotted")+
geom_text(data=IE.BIR_H_Max,aes(label=obsvalue,x=obstime,y=obsvalue,group=ref_area),colour="black",alpha=0.6,size=2.75,hjust="left")
SVL %>%
ggplot(aes(x=obstime,y=obsvalue,group=area,colour=area))+
geom_line(size=1.05,alpha=0.5)+
scale_colour_manual(values=c("navy","darkgreen"))+
geom_text_repel(aes(label=lab),max.overlaps = 5,alpha=0.35,size=3)+
geom_text(data=SVL_Max,aes(label=lab),colour="black",alpha=0.9,size=3,hjust="right",fontface="bold")+
theme_bw()+
theme(legend.position="bottom",
plot.title = element_text(face="bold"))+
xlab(NULL)+
ylab("percent (%)")+
ggtitle("Share of Variable Rate Loans in Total Loans")
In Ireland, the share of variable rate loans has halved year to date.
SVL_20 <- SVL %>%
filter(obstime>="2020-01-01")
dates_vline <- as.Date("2022-07-01")
dates_vline <- which(SVL_20$obstime %in% dates_vline)
SVL_20 %>%
ggplot(aes(x=obstime,y=obsvalue,group=area,colour=area))+
geom_line(size=1.05,alpha=0.5)+
scale_colour_manual(values=c("navy","darkgreen"))+
geom_text_repel(aes(label=lab),max.overlaps = 5,alpha=0.35,size=3)+
geom_text(data=SVL_Max,aes(label=lab),colour="black",alpha=0.9,size=3,hjust="right",fontface="bold")+
theme_bw()+
theme(legend.position="bottom",
plot.title = element_text(face="bold"))+
xlab(NULL)+
ylab("percent (%)")+
ggtitle("Signs of Switching?")+
labs(subtitle="Share of Variable Rate Loans in Total Loans",caption="Red line: first ECB rate hike of the cycle")+
geom_vline(xintercept = as.numeric(SVL_20$obstime[dates_vline]),colour="darkred",linetype="dotted")
colour <-ifelse(WPM28_B$value < 0,"#CC0000","#1b5545")
ggplot(WPM28_B, aes(x=Date, y=value, group=Type.of.Material))+
geom_col(aes(group=Type.of.Material), alpha = 0.65, colour="#373634", fill = colour)+
labs(title = "Percentage Change over 12 month in Wholesale Price Index")+
xlab("Year-Month")+
ylab("Percentage change")+
# scale_x_date(date_labels="%b-%Y",date_breaks ="3 month")+
geom_text_repel(aes(label=value),data = WPM28_B, size = 3)+
theme_bw()+
theme(axis.text.x = element_text(angle=90))+
theme(axis.text.x=element_text(size=10))+
theme(legend.position="none")+
theme(axis.text = element_text(size = rel(1)))+
theme(plot.title=(element_text(vjust =2)))+
theme(panel.border = element_rect(linetype = 1, fill = NA))+
facet_wrap(~Type.of.Material, ncol = 3)
Black line (100) indicates 2015 as baseline
WPM28_A_Max <- WPM28_A %>%
group_by(Type.of.Material) %>%
slice(which.max(Date))
WPM28_A_Max$lab <- paste(WPM28_A_Max$Month,":",WPM28_A_Max$value)
ggplot(WPM28_A, aes(x=Date, y=value, group=Type.of.Material, colour=Type.of.Material))+
geom_line(aes(group=Type.of.Material),size = 1.05, linetype=1, alpha = 0.65)+geom_text_repel(aes(label=value),data = WPM28_A, size = 3)+
labs(title = "Percentage Change over 12 month in Wholesale Price Index")+
xlab("Year-Month")+
ylab("2015 = 100")+
geom_hline(aes(yintercept=100),
colour= "#404040",
linetype = 1)+
# scale_x_date(date_labels="%b-%Y",date_breaks ="3 month")+
theme_bw()+
theme(axis.text.x = element_text(angle=90))+
theme(axis.text.x=element_text(size=10))+
theme(legend.position="none")+
theme(axis.text = element_text(size = rel(1)))+
theme(plot.title=(element_text(vjust =2)))+
theme(panel.border = element_rect(linetype = 1, fill = NA))+
geom_text_repel(data=WPM28_A_Max,aes(label=lab,y=160),colour="black",alpha=0.2,size=3)+
facet_wrap(~Type.of.Material, ncol = 3,labeller = labeller(Type.of.Material = label_wrap_gen(width = 40)))
The below is useful to get an idea of the proportion of government debt and expenditure relative to income.
####################################################### General Government Net Worth, Gross and Net Debt ESA 2010
# Load & Clean
GFQ12 <- cso_get_data("GFQ12")
GFQ12 <- GFQ12 %>%
pivot_longer(!1:2,names_to="year_qtr")
GFQ12$Year_Q <- as.yearqtr(GFQ12$year_qtr)
GFQ12$Year <- year(GFQ12$Year_Q)
GFQ12_Max <- GFQ12 %>%
slice(which.max(Year_Q))
GFQ12_Max$lab <- paste(GFQ12_Max$Year_Q,": ",GFQ12_Max$value,"%")
#Plot
GFQ12 %>%
ggplot(aes(x=Year_Q,y=value))+
geom_line(size=1.25,colour="darkgreen",alpha=0.6)+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(face="bold"))+
ggtitle("Gross Government Debt to GDP")+
ylab("%")+
xlab(NULL)+
labs(subtitle="GFQ12: Debt ESA2010 as a Percentage of the Annualised Gross Domestic Product")+
geom_text_repel(data=GFQ12_Max,aes(label=lab),colour="darkgreen",fontface="bold")
# Plot
GFQ01.SUR %>%
ggplot(aes(x=Year_Q,y=value,group=Item))+
geom_hline(yintercept = 0,colour="darkred",alpha=0.5)+
geom_line(aes(colour=Item,linetype=Item),size=1,alpha=0.7)+
scale_colour_manual(values=c("#6C7272","#09716D"))+
geom_line(aes(y=SMA.12,linetype=Item),colour="#027100",alpha=0.2)+
theme_bw()+
theme(legend.position = "bottom",
plot.title = element_text(face="bold"),
legend.key.size = unit(1, 'cm'),
legend.key.height = unit(0.5, 'cm'),
legend.key.width = unit(0.5, 'cm'),
legend.title = element_text(size=8),
legend.text = element_text(size=8))+
ggtitle("General Government Surplus & Deficit")+
ylab("euro million")+
xlab(NULL)+
labs(caption="thin green line represents the 12 month moving average")
GFQ01.REV %>%
ggplot(aes(x=Year_Q,y=value,group=Item))+
geom_line(aes(colour=Item),alpha=0.4)+
geom_line(aes(y=SMA.12,colour=Item),alpha=0.8)+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(face="bold"))+
ggtitle("General Government Transactions: Revenue")+
ylab("euro million")+
xlab(NULL)+
labs(subtitle="12 month moving average")+
facet_wrap(~Item,ncol=2,labeller = labeller(Item = label_wrap_gen(width = 55)))+
geom_text(data=GFQ01.REV_Max,aes(x=Year_Q,y=10000,label=lab,colour=Item),hjust="right")
GFQ01.EXP %>%
ggplot(aes(x=Year_Q,y=value,group=Item))+
geom_line(aes(colour=Item),alpha=0.4)+
geom_line(aes(y=SMA.12,colour=Item),alpha=0.8)+
scale_colour_brewer(palette = "Set3")+scale_colour_hue(l = 45)+
theme_bw()+
theme(legend.position = "none",
plot.title = element_text(face="bold"))+
ggtitle("General Government Transactions: Expenditure")+
ylab("euro million")+
xlab(NULL)+
labs(subtitle="12 month moving average")+
facet_wrap(~Item,ncol=2,labeller = labeller(Item = label_wrap_gen(width = 55)))+
geom_text(data=GFQ01.EXP_Max,aes(x=Year_Q,y=11000,label=lab,colour=Item),hjust="right")
# Step 1. Load Commodity Groups (local) and Value of Merchandise Trade from the CSO
CG <- readxl::read_xlsx(path="C:\\Users\\charten\\OneDrive - Glenveagh Properties\\Research & Development\\1. Analysis\\Commodity Groups.xlsx",sheet = 1)
TSM10 <- cso_get_data("TSM10")
TSM10 <- TSM10 %>%
pivot_longer(!1:3,names_to="year_month")
TSM10$Year <-substr(TSM10$year_month,1,4)
TSM10$Month <- sub(".* ", "", TSM10$year_month)
TSM10$Month_NR <- as.integer(factor(TSM10$Month, levels=month.name))
TSM10$Date <- as.yearmon(paste(TSM10$Year, TSM10$Month_NR), "%Y %m")
# Step 2. Create Subsets
## 2.1 Total merchandise trade (0 - 9)
TSM10.1 <- TSM10 %>%
filter(Commodity.group=="Total merchandise trade (0 - 9)")
### Total
TSM10.1.T <- TSM10.1 %>%
group_by(Statistic,Commodity.group,year_month,Year,Date) %>%
summarise(value = sum(value,na.rm = TRUE))
TSM10.1.T <- TSM10.1.T[order(TSM10.1.T$Statistic,TSM10.1.T$Date),]
TSM10.1.T$SMA.6 <- round(SMA(TSM10.1.T$value,n=6),digits=2)
TSM10.1.T$SMA.6[TSM10.1.T$Date=="Jan 2015" | TSM10.1.T$Date=="Feb 2015" | TSM10.1.T$Date=="Mar 2015"|TSM10.1.T$Date=="Apr 2015" | TSM10.1.T$Date=="May 2015"] <- NA
## 2.2 Exclude Total Merchandise Trade & Unclassified estimates
TSM10.2 <- TSM10 %>%
filter(Commodity.group!="Total merchandise trade (0 - 9)") %>%
filter(Commodity.group!="Unclassified estimates")
TSM10.2.T <- TSM10.2 %>%
group_by(Statistic,Commodity.group,year_month,Year,Date) %>%
summarise(value = sum(value,na.rm = TRUE))
TSM10.2.T <- TSM10.2.T[order(TSM10.2.T$Statistic,TSM10.2.T$Commodity.group,TSM10.2.T$Date),]
TSM10.2.T$SMA.6 <- round(SMA(TSM10.2.T$value,n=6),digits=2)
TSM10.2.T$SMA.6[TSM10.2.T$Date=="Jan 2015" | TSM10.2.T$Date=="Feb 2015" | TSM10.2.T$Date=="Mar 2015"|TSM10.2.T$Date=="Apr 2015" | TSM10.2.T$Date=="May 2015"] <- NA
TSM10.2.T <- merge(x=TSM10.2.T,y=CG,by.x="Commodity.group",by.y="Commodity.Group")
TSM10.2.T <- TSM10.2.T[order(TSM10.2.T$Statistic,TSM10.2.T$Commodity.group,TSM10.2.T$Date),]
## Subset by Group
TSM10.2.T.0 <- TSM10.2.T %>%
filter(Group=="0")
TSM10.2.T.1 <- TSM10.2.T %>%
filter(Group=="1")
TSM10.2.T.2 <- TSM10.2.T %>%
filter(Group=="2")
TSM10.2.T.3 <- TSM10.2.T %>%
filter(Group=="3")
TSM10.2.T.4 <- TSM10.2.T %>%
filter(Group=="4")
TSM10.2.T.5 <- TSM10.2.T %>%
filter(Group=="5")
TSM10.2.T.6 <- TSM10.2.T %>%
filter(Group=="6")
TSM10.2.T.7 <- TSM10.2.T %>%
filter(Group=="7")
TSM10.2.T.8 <- TSM10.2.T %>%
filter(Group=="8")
TSM10.2.T.9 <- TSM10.2.T %>%
filter(Group=="9")
# Step 3. Plot
options(scipen = 999)
TSM10.1.T_max <- TSM10.1.T %>%
group_by(Statistic)%>%
slice(which.max(Date))
TSM10.1.T_max$lab_SMA.6 <- paste(as.character(TSM10.1.T_max$Date), "-", comma(TSM10.1.T_max$SMA.6,digits=0))
TSM10.1.T_max$lab_value <- comma(TSM10.1.T_max$value,digits=0)
TSM10.1.T %>%
ggplot(aes(x=Date,y=SMA.6,group=Statistic,colour=Statistic))+
geom_text(data=TSM10.1.T_max,aes(label=lab_SMA.6),size=2.75,hjust=1.15,vjust=1,colour="black")+
geom_text(data=TSM10.1.T_max,aes(y=value,label=lab_value),size=2.5,colour="#616161")+
geom_line(aes(x=Date,y=value,group=Statistic,colour=Statistic),alpha=0.25)+
geom_line()+
theme_bw()+
theme(legend.position = "bottom")+
ggtitle("Value of Merchandise Trade")+
labs(subtitle="CSO: TSM10 - Total merchandise",caption="latest month actual value printed in grey at the end of the monthly series")+
ylab("6 month moving average - (Euro Thousand)")+
scale_y_continuous(labels = scales::number_format(accuracy = 1,decimal.mark = ','))