Пакет RStudio для загрузки банковской отчетности с сайта ЦБ РФ

Пакет RStudio для загрузки банковской отчетности с сайта ЦБ РФ

Задача: изучить динамику финансовых показателей российских банков

В России все банки обязаны отчитываться перед Центральном банком РФ. Делают они это в пяти формах:

  • Форма 101 – данные оборотной ведомости по счетам бухгалтерского учёта
  • Форма 102 – отчёт о финансовых результатах
  • Форма 123 – расчёт собственных средств (капитала) («Базель III»)
  • Форма 134 – Расчёт собственных средств (капитала)
  • Форма 135 – информация об обязательных нормативах

На сайте ЦБ эти отчетности хранятся в архивированных файлах баз данных в формате *.dbf. Причем формат баз данных часто менялся, даже формат архива не универсален: до 2009 года *.zip, после – *.rar.

Без имени-1

Пакет должен умеет разархивировать и считать dbf-файлы. Для этого пользователю понадобится установить UnRAR с сайта RARLAB. Для чтения dbf-файлов используем foreign::read.dbf, для парсинга сайта ЦБ — XML::readHTMLTable.

Пишем функции для получения отчетности

  • cbrAPI::download.dbf возвращает отчетность за выбранный период
  • cbrAPI::download.dbf.all возвращает отчетность за всю доступную историю
  • cbrAPI::getBankInfo возвращает информацию о банке
Снимок экрана 2019-01-22 в 12.38.08
Снимок экрана 2019-01-22 в 12.38.20
Снимок экрана 2019-01-22 в 12.42.04
Снимок экрана 2019-01-22 в 12.41.27

Смотрим объем на межбанке

Скачиваем 101 форму за весь доступный период

library(cbrAPI)
data101 <- download.dbf.all(form = 101)

До 2007 года суммы по счетам хранились в столбце ITOGO, после — в столбце IITG. Нужно исправить это, соединив в одну колонку.

data <- data101[ , which(names(data101) %in% c('REGN','NUM_SC','ITOGO','IITG','A_P','DT'))]
x <- subset(data, DT < '2007-01-01')
y <- subset(data, DT >= '2007-01-01')
data$ITOGO <- c(x$ITOGO, y$IITG)
data <- data[ , -which(names(data) %in% c('IITG'))]
rm(x,y)

Возьмем какой-нибудь банк, например, Сбербанк

data.small <- subset(data, REGN == 1481)

Умножим активы на -1, чтобы найти сумму. Строки ITGAP (суммы по активам / пассивам) заполнятся NA — это не страшно.

data.small$NUM_SC <- as.integer(as.character(data.small$NUM_SC))
data.small$ITOGO <- as.integer(as.character(data.small$ITOGO))
x <- subset(data.small, subset=A_P==1)
y <- subset(data.small, subset=A_P==2)
x$ITOGO <- x$ITOGO * (-1)
data.small <- rbind(x,y)
data.small <- data.small[with(data.small, order(DT, REGN, NUM_SC)), ]
rm(x,y)

Находим объем пассивов на межбанке — это 300 - 329 пассивные счета

data.small <- subset(data.small, subset=A_P==2)
data.small <- subset(data.small, subset=(NUM_SC %in% c(30000:32902)))
data.sub <- matrix(nrow=0,ncol=2)
colnames(data.sub) <- c("DT", "Corporate")
for (i in levels(as.factor(data.small$DT))) {
corporate <- subset(data.small, subset=(DT==i))
data.sub <- rbind(data.sub, c(i, sum(corporate$ITOGO)))
}
data.sub <- as.data.frame(data.sub)
rm(corporate)

Рисуем график стандартными средствами

barplot(as.numeric(as.character(data.sub$Corporate))/1000000,
main=paste("Bar Chart of", getBankInfo(data.small$REGN[1])$name),
xlab="Date, months", ylab="Volume, millions of rubles",
names.arg=data.sub$DT)

1

Объединяем данные 101 и 102 форм

Скачиваем 101 форму за весь доступный период

library(cbrAPI)
data101 <- download.dbf.all(form = 101)

Берем банковский капитал, он на 102 - 109 счетах

data101 <- subset(data101, NUM_SC %in% c(10200:10999))

До 2007 года суммы по счетам хранились в столбце ITOGO, после — в столбце IITG. Нужно исправить это, соединив в одну колонку.

data101 <- data101[ , which(names(data101) %in% c('REGN','NUM_SC','ITOGO','IITG','A_P','DT'))]
x <- subset(data101, DT < '2007-01-01')
y <- subset(data101, DT >= '2007-01-01')
data101$ITOGO <- c(x$ITOGO, y$IITG)
data101 <- data101[ , -which(names(data101) %in% c('IITG'))]
rm(x,y)

Умножим активы на -1, чтобы найти сумму. Строки ITGAP (суммы по активам / пассивам) заполнятся NA — это не страшно.

data101$NUM_SC <- as.integer(as.character(data101$NUM_SC))
data101$ITOGO <- as.integer(as.character(data101$ITOGO))
x <- subset(data101, subset=A_P==1)
y <- subset(data101, subset=A_P==2)
x$ITOGO <- x$ITOGO * (-1)
data101 <- rbind(x,y)
data101 <- data101[with(data101, order(DT, REGN, NUM_SC)), ]
rm(x,y)

Суммируем данные по каждому банку за каждую дату

data101.sub <- matrix(nrow=0,ncol=3)
colnames(data101.sub) <- c("REGN", "DT", "Equity")
regnums1 <- levels(as.factor(data101$REGN))
dates1 <- levels(as.factor(data101$DT))
for (i in regnums1) {
sub <- subset(data101, REGN == i)
for (j in dates1) {
equity <- subset(sub, DT == j)
equity <- sum(equity$ITOGO)
data101.sub <- rbind(data101.sub, c(i, j, equity))
}
message(i, ' / ', regnums1[length(regnums1)])
}
data101.sub <- as.data.frame(data101.sub)
rm(sub, equity, i, j)

Берем процентные доходы банков, они находятся в 102 форме на 11ххх счетах

data102 <- download.dbf.all(form = 102)
data102 <- subset(data102, CODE %in% c(11100:11999))

В 102 форме до 2007 года суммы по счетам тоже хранились в столбце ITOGO, после — в столбце SIM_ITOGO. Нужно это тоже исправить.

x <- subset(data102, DT < '2007-01-01')
y <- subset(data102, DT >= '2007-01-01')
data102$ITOGO <- c(x$ITOGO, y$SIM_ITOGO)
data102 <- data102[ , -which(names(data102) %in% c('SIM_R','SIM_V','SIM_ITOGO'))]
rm(x,y)

Суммируем данные 102 формы по каждому банку за каждую дату

data102.sub <- matrix(nrow=0,ncol=3)
colnames(data102.sub) <- c("REGN", "DT", "Income")
regnums2 <- levels(as.factor(data102$REGN))
dates2 <- levels(as.factor(data102$DT))
for (i in regnums2) {
sub <- subset(data102, REGN == i)
for (j in dates2) {
income <- subset(sub, DT == j)
income <- sum(income$ITOGO)
data102.sub <- rbind(data102.sub, c(i, j, income))
}
message(i, ' / ', regnums2[length(regnums2)])
}
data102.sub <- as.data.frame(data102.sub)
rm(sub, income, i, j)

Берем общие регистрационные номера

regnums <- subset(regnums1, regnums1 %in% regnums2)
rm(regnums1, regnums2)

Суммируем каждые три даты, чтобы собрать квартальные отчеты и прикрепить их в столбец к data.sub

data.plot <- matrix(nrow=0,ncol=5)
colnames(data.plot) <- c("REGN", "DT", "Equity", "Income", "Coef")
for (i in regnums) {
sub <- subset(data101.sub, REGN == i)
sub$DT <- as.character(sub$DT)
for (j in 2:length(dates2)) {
equity <- subset(sub, DT <= dates2[j])
equity <- subset(equity, DT > dates2[j-1])
equity$Equity <- as.numeric(as.character(equity$Equity))
equity <- sum(equity$Equity)
income <- subset(data102.sub, REGN == i & DT == dates2[j])
income$Income <- as.numeric(as.character(income$Income))
income <- sum(income$Income)
data.plot <- rbind(data.plot, c(i, dates2[j], equity, income, income/equity))
}
message(i, ' / ', regnums[length(regnums)])
}
data.plot <- as.data.frame(data.plot)
rm(sub, equity, income, i, j)

Приготавливаем данные для графиков, логарифмируем и округляем коэффициенты

df <- data.plot
df$Coef <- as.numeric(as.character(df$Coef))
df$Coef <- log(df$Coef)
df$Coef <- round(df$Coef, 4)
# уберем все нулевые строки
df <- df[apply(df, 1, function(row) all(row !=0 )),]
df$DT <- as.Date(df$DT, format='%Y-%m-%d')

Теперь рисуем диаграмму размаха (box-and-whiskers plot), используя библиотеку ggplot2. При просмотре графиков стоит учитывать, что, например, отчетность за 1 января 2015 года — отчетность за последний квартал 2014 года.

library("ggplot2")
labels <- rep('', length(dates2))
labels[seq(4,length(dates2),4)] <- 2005:2015
plot <- ggplot(data=df, aes(factor(DT), Coef))
plot + ggtitle('Box plot') +
scale_x_discrete(name='Coefficient', labels=labels) +
scale_y_discrete(name='Date') +
geom_boxplot()

2

Смотрим прибыль банков

Скачиваем 101 форму за весь доступный период

library(cbrAPI)
data101 <- download.dbf.all(form = 101)

Берем для примера какую-нибудь дату

data <- download.dbf("2004-06")

Оставляем только нужные нам колонки

data <- data[ , which(names(data) %in% c('REGN','NUM_SC','ITOGO','A_P','DT'))]
head(data)

Умножим активы на -1, чтобы найти сумму. Строки ITGAP (суммы по активам / пассивам) заполнятся NA — это не страшно.

data$NUM_SC <- as.integer(as.character(data$NUM_SC))
data$ITOGO <- as.integer(as.character(data$ITOGO))
x <- subset(data, subset=A_P==1)
y <- subset(data, subset=A_P==2)
x$ITOGO <- x$ITOGO * (-1)
data <- rbind(x,y)
data <- data[with(data, order(DT, REGN, NUM_SC)), ]
rm(x,y)

Найдем прибыль каждого банка, до 2008 года она хранилась на 701 - 704 счетах, после — на 706.

sub <- subset(data, subset=(NUM_SC %in% c(70100:70499, 70600:70699)))
data.sub <- matrix(nrow=0,ncol=2)
colnames(data.sub) <- c("REGN", "Profit")
for (i in levels(as.factor(data$REGN))) {
profit <- subset(sub, subset=(REGN==i))
data.sub <- rbind(data.sub, c(i, sum(profit$ITOGO)))
}
data.sub <- as.data.frame(data.sub)
rm(sub, profit)

Берем топ-5, остальные объединяем

data.sub$REGN <- as.numeric(as.character(data.sub$REGN))
data.sub$Profit <- as.numeric(as.character(data.sub$Profit))
data.plot <- data.sub[with(data.sub, order(-Profit)), ]
data.plot <- rbind(data.plot[1:5,], c(NA, sum(data.plot[6:nrow(data.plot),])))

Подгружаем имена банков для графика

for (i in 2:nrow(data.plot)-1) {
message(i)
data.plot$REGN[i] <- as.character(getBankInfo(data.plot[i,1])$name)
}
data.plot[nrow(data.plot),1] <- 'остальные'
colnames(data.plot)[1] <- 'Name'
rownames(data.plot) <- NULL

Рисуем круговую диаграмму с помощью gglot2

library("ggplot2")
ggplot(data.plot, aes(x='', y=Profit, fill=Name))+
geom_bar(width = 1, stat = "identity") +
coord_polar("y", start=0) +
scale_x_discrete('') +
ggtitle(paste("Stacked bar chart", data[1,5])) +
theme(axis.ticks = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_blank())

3

Сравниваем несколько категорий счетов

Скачиваем 101 форму за весь доступный период

library(cbrAPI)
data101 <- download.dbf.all(form = 101)

До 2007 года суммы по счетам хранились в столбце ITOGO, после — в столбце IITG. Нужно исправить это, соединив в одну колонку.

data <- data101[ , which(names(data101) %in% c('REGN','NUM_SC','ITOGO','IITG','A_P','DT'))]
x <- subset(data, DT < '2007-01-01')
y <- subset(data, DT >= '2007-01-01')
data$ITOGO <- c(x$ITOGO, y$IITG)
data <- data[ , -which(names(data) %in% c('IITG'))]
rm(x,y)

Возьмем какой-нибудь банк, например, ВТБ

data.small <- subset(data, REGN == 1623)

Умножим активы на -1, чтобы найти сумму. Строки ITGAP (суммы по активам / пассивам) заполнятся NA — это не страшно.

data.small$NUM_SC <- as.integer(as.character(data.small$NUM_SC))
data.small$ITOGO <- as.integer(as.character(data.small$ITOGO))
x <- subset(data.small, subset=A_P==1)
y <- subset(data.small, subset=A_P==2)
x$ITOGO <- x$ITOGO * (-1)
data.small <- rbind(x,y)
data.small <- data.small[with(data.small, order(DT, REGN, NUM_SC)), ]
rm(x,y)

Берем несколько категорий счетов:

  • депозиты физ. лиц (423, 426 счета)
  • депозиты юр. лиц (420 - 422 and 425 счета)
  • капитал (100 пассивные счета)

sub1 <- subset(data.small, subset=(NUM_SC %in% c(42300:42399, 42600:42699)))
sub2 <- subset(data.small, subset=(NUM_SC %in% c(42000:42299, 42500:42599)))
sub3 <- subset(data.small, subset=A_P==2)
sub3 <- subset(data.small, subset=(NUM_SC %in% c(10200:10999)))

Суммируем

data.sub <- matrix(nrow=0,ncol=3)
colnames(data.sub) <- c("DT", 'Type', "Volume")
for (i in levels(as.factor(data.small$DT))) {
individuals <- subset(sub1, subset=(DT==i))
data.sub <- rbind(data.sub, c(i, 'individuals', sum(individuals$ITOGO)))
corporate <- subset(sub2, subset=(DT==i))
data.sub <- rbind(data.sub, c(i, 'corporate', sum(corporate$ITOGO)))
equity <- subset(sub3, subset=(DT==i))
data.sub <- rbind(data.sub, c(i, 'equity', sum(equity$ITOGO)))
}
data.sub <- as.data.frame(data.sub)
rm(sub1, sub2, sub3, individuals, corporate, equity)

Подготавливаем данные для графиков: берем данные за один год и выражаем в миллионах рублей

data.plot <- data.sub
data.plot$DT <- as.character(data.plot$DT)
data.plot$Type <- as.character(data.plot$Type)
data.plot$Volume <- as.numeric(as.character(data.plot$Volume))/1000000
data.plot <- subset(data.plot, subset=(DT >= '2014-01-01' & DT < '2015-01-01'))

Рисуем столбчатую диаграмму с накоплением с помощью gglot2

ggplot(data=data.plot, aes(x=DT, y=Volume, fill=Type)) +
geom_bar(stat="identity") +
scale_y_discrete(breaks=seq(0, 5000, 300), 'Volume, millions of rubles') +
scale_x_discrete(breaks=levels(as.factor(data.plot$DT))[seq(1,12,3)], 'Date') +
ggtitle(paste("Stacked bar chart of", getBankInfo(data.small$REGN[1])$name))

4

Хочу так же