Краткое повторение ⏭

Моя предыдущая заметка была посвящена неопределенности наличия рецессии в США, которая закончилась тем, что Федеральный Резерв США, выполняющий функции центрального банка, предлагает ориентироваться на экономитрическую модель при оценке вероятности наличия рецессии1. Автором данной модели – является Marcelle Chauvet, которая заслужила место в тизере к данной заметке не благодаря своей модельной внешности, но благодаря математическим способностям. Надеюсь, ни один ноготь безупречного маникюра Marcelle не пострадал при набивании кода модели в MatLab. Впрочем, публичного кода модели я найти не смог, а это было бы неплохим подспорьем в моих изысканиях.

Данная заметка будет посвящена небольшому исследованию спецификации модели и проблематике, которую такая модель решает. Для комфортного чтения читатель должен быть знаком с основными экономитрическими концепциями. В этот раз я будут использовать более производительные пакеты обработки данных, поэтому для кого-то синтаксис кода покажется не очень знакомым с одной стороны, но позволит подсмотреть менее популярные схемы обработки данных и, возможно, взять их на заметку когда читатель столкнется с более-менее большими массивами информации.

library(data.table) # быстрый пакет для работы с табличными данными
library(collapse) # быстрый пакет для работы с табличными данными
library(stringi) # пакет для работы с текстовой информацией 
library(DT) # пакет создания интерактивных таблиц
library(echarts4r) # интерактивные графики
library(firatheme) # палитра, которую я использую для непрерывных индикаторов
library(emphatic) # пакет для раскрашивания матриц и табличек для наглядности 

# Сохраняем палитру в отдельную переменную
my_pal <- palette.colors(palette = "Tableau") %>% unname() 

Предикторы рецессии 🔮

Согласно описанию модели:

Smoothed recession probabilities for the United States are obtained from a dynamic-factor markov-switching model applied to four monthly coincident variables: non-farm payroll employment, the index of industrial production, real personal income excluding transfer payments, and real manufacturing and trade sales.

Итак имеется в описании модель динамического изменения факторов на основе модели переключения Маркова и четыре предиктора. Описание хорошо корреспондируется с определением рецессии, которое давалось в предыдущей заметки:

существенное снижение экономической активности на рынках, которое длиться более нескольких месяцев, обычно наблюдаемое на реальном ВВП, занятости, промышленном производстве, розничных продажах

Другими словами, для воспроизведения модели необходимо иметь следующие данные:

Код Описание Пояснение
1 PAYEMS All Employees, Total Nonfarm Занятость, выраженная в количестве работающего персонала
2 INDPRO Industrial Production: Total Index Индустриальное производство
3 W875RX1 Real personal income excluding current transfer receipts Доходы населения, исключая трансферты
4 CMRMTSPL Real Manufacturing and Trade Industries Sales Реальные продажи производящих отраслей
5 USRECD NBER based Recession Indicators for the United States from the Period following the Peak through the Trough Индикатор рецессии, что является целевой переменной
6 RECPROUSM156N Smoothed U.S. Recession Probabilities Оценка вероятности рецессии, предоставляемая ФРС – FRED

Пункт 5 – является булевым показателем, который необходимо предсказывать, а пункт 6 – представляет собой оценку модели, которую я попытаюсь воспроизвести.

Прежде чем перейти к анализу данных хорошо бы понять мотивы почему именно этот набор показателей – является определяющим в вопросе рецессии. Как известно, ВВП может быть рассчитан несколькими способами: по доходам и расходам, что весьма логично т.к. потратить можно лишь то, что было заработано, а баланс доходов и расходов должен сходиться. Иными словами, весь валовый продукт, созданный экономикой будет равен продукту, который эта экономика расходует с точностью до баланса запасов капитала, которые следует трактовать в широком смысле как основные и оборотные фонды. Итак ВВП может быть рассчитан с помощью добавленной стоимости по следующей формуле:

ВВП =

  • Оплата труда +
  • Прибыль предприятий +
  • Амортизация +
  • Прямые налоги +
  • Косвенные налоги с учетом субсидий

Далее фундаментально можно предположить:

  1. Оплата труда корреспондируется с Занятостью и Доходами населения
  2. Прибыль предприятий и Амортизация корреспондируется с Производством и Продажами
  3. Прямые и косвенные налоги – есть величины взимаемые с деятельности двух первых пунктов

К сожалению, вопрос выбора именно таких предикторов для прогноза никаким образом не раскрыт в статье Marcelle Chauvet2. Вероятно, такой вопрос не задают в приличном обществе. Мое мнение на этот счет предельно простое: данный набор переменных – является оперативными статистическими сведениями, которые доступны по истечении прошедшего месяца и других данных такого же уровня оперативности просто не существует. В любом случае состоятельность выбора предикторов предстоит проверить техническими средствами через условие экзогенности модели.

Данные 💿

После небольшой предварительной подготовки данных, которая заключалась в приведении дневных значений рецессии в помесячные получился достаточно компактный и приятный для работы набор данных. Ранее я уже упоминал, что любое официальное начало рецессии всегда выпадало на начало месяца, а завершение соответственно на конец месяца и следовательно дневная детализация не имеет совершенно никакого смысла.

# Загрузка подготовленных данных
indctrs0 <- fst::read_fst("data/indctrs.fst", as.data.table = TRUE)

datatable(indctrs0[1:30], style = 'bootstrap4', extensions = 'Responsive', 
          options = list(pageLength = 6), caption = "Подготовленные данные")

Естественно, было бы неплохо визуализировать данные и прежде всего посмотреть на то как модель Marcelle Chauvet делает оценку вероятности рецессии:

indctrs0 |> tfm(RECPROUSM156N = RECPROUSM156N/100) |>
  e_charts(date) |> 
  e_area(USRECD, symbol= 'none', lineStyle = list(width = 0), 
         name = "Период рецессии", color = "rgba(225, 87, 89, .5)") |> 
  e_line(RECPROUSM156N, symbol= 'none', color = my_pal[1], 
         name = "Вероятность рецессии") |>
  e_datazoom(x_index = 0, type = "slider") |>
  e_title(text = "Оценка вероятности рецесии в США") |> 
  e_y_axis(name = "Вероятность рецесии", formatter = e_axis_formatter("percent")) |> 
  e_x_axis(name = "Месяц") |> 
  e_draft("InvestCookies.ru", size = "80px", opacity = 0.2) |> 
  e_legend(padding = 30) |> 
  e_tooltip(trigger = "axis", axisPointer = list(type = "line"), 
            backgroundColor = "rgba(255,255,255,0.7)", 
            formatter = e_tooltip_pointer_formatter("percent")) 

Надо сказать, что модель вполне прилично справляется с определением периодов сжатия в экономики, допуская лишь незначительные ложно положительные огрехи. Выглядит практически идеально.

Прежде чем визуализировать независимые переменные-предикторы необходимо сделать небольшие преобразования, а именно посчитать изменения значений в процентном выражении и добавить сдвинутые на один период переменные. Суффикс _ch – будет означать изменение, _lg – будет означать сдвиг _chlg – будет означать сдвиг изменения:

preds <- c("PAYEMS", "INDPRO", "W875RX1", "CMRMTSPL")

indctrs1 <- indctrs0 |> 
  fselect(-RECPROUSM156N) %>% 
  # Получение относительных изменений
  .[, stri_c(preds, "_ch") := lapply(.SD, \(x) shift((x - shift(x))/x)), .SDcols = preds]  %>% 
  # Получение сдвига на один период
  .[, stri_c(c(preds, stri_c(preds, "_ch")), "lg") := lapply(.SD, shift), .SDcols = c(preds, stri_c(preds, "_ch"))] |> 
  na_omit()

datatable(indctrs1[1:30], style = 'bootstrap4', extensions = 'Responsive', 
          options = list(pageLength = 6), caption = "Индикаторы рецесси")

Теперь непосредственно визуализация с зумом для того чтобы можно было подробнее рассмотреть периоды сваливания в рецессию. Предикторы были нормированы на интервал от 0 до 1 для того чтобы сделать график более наглядным:

tfmv(indctrs1, vars = preds, FUN = \(x)scales::rescale(x, c(0, 1))) |> 
  e_charts(date) |> 
  e_area(USRECD, symbol= 'none', lineStyle = list(width = 0), 
         name = "Период рецессии", color = "rgba(225, 87, 89, .5)") |>
  e_line(PAYEMS, symbol= 'none', name = "Занятость") |>
  e_line(INDPRO, symbol= 'none', name = "Производство") |>
  e_line(W875RX1, symbol= 'none', name = "Доходы населения") |>
  e_line(CMRMTSPL, symbol= 'none', name = "Продажи") |>
  e_line(PAYEMS_ch, symbol= 'none', x_index = 2, y_index = 2, name = "Занятость", 
         lineStyle = list(opacity = .5)) |>
  e_line(INDPRO_ch, symbol= 'none', x_index = 2, y_index = 2, name = "Производство", 
         lineStyle = list(opacity = .5)) |>
  e_line(W875RX1_ch, symbol= 'none', x_index = 2, y_index = 2,  name = "Доходы населения", 
         lineStyle = list(opacity = .5)) |>
  e_line(CMRMTSPL_ch, symbol= 'none', x_index = 2, y_index = 2,  name = "Продажи",
         lineStyle = list(opacity = .5)) |>
  e_color(my_pal) |>
  e_grid(height = "30%", top = "60%") |> 
  e_grid(height = "35%") |>
  e_datazoom(type = "slider", xAxisIndex = "all") |>
  e_y_axis(gridIndex = 1, name = "Динамика предикторов", 
           formatter = e_axis_formatter("percent")) |> 
  e_y_axis(index = 2, name = "Относительная\nдинамика предикторов", 
           formatter = e_axis_formatter("percent"), min = -.15, max = .1) |>
  e_x_axis(name = "Месяц") |> 
  e_x_axis(gridIndex = 1) |>
  e_draft("InvestCookies.ru", size = "80px", opacity = 0.2) |> 
  e_legend(type = "scroll", selector = TRUE, yAxisIndex = "all") |> 
  e_tooltip(trigger = "axis", 
            backgroundColor = "rgba(255,255,255,0.7)", 
            formatter = e_tooltip_pointer_formatter("percent")) 

Первое на, что можно обратить внимание – это то, что в середине века рецессии были более частым явлением чем в последнее время. Некоторое количество кризисных периодов отличается от прочих:

  • Рецессия в период кризиса доткомов 2001 года состоялась спустя пол года после начала падения предикторов
  • Рецессия, вызванная пандемией короновируса была очень резкой как по падению так и по восстановлению

В целом нельзя сказать, что выбранные предикторы дают какой-то заблаговременный сигнал о приближающейся рецессии, но скорее способны помочь определить факт наличия рецессии, что собственно сейчас и нужно. Также следует обратить внимание на то, что относительная динамика предикторов зачастую более волатильная в период рецессии.

Простая модель 🐵

Безусловной целью моделирования – является прогноз и его характеристика точности. С другой стороны, не нужно забывать, что данные сами по себе не содержат сути, которую можно выявить лишь с помощью интерпретации. Поэтому прежде чем переходить к технике вопроса хорошо бы сложить впечатление о сути для чего будет использована простая модель логистической регрессии. В основе логистической регрессии лежит линейная регрессия, которая дает несмещенность оценок. Это свойство является полезным при анализе коэффициентов модели.

Думаю, разумно будет разбить все данные на две части:

  • Первую часть до начала 2022 года будет использована для обучении модели
  • Вторая часть, которая начинается с 2022 года будет предсказываться

Модель логистической регрессии по своей природе невозможно переобучить – поэтому формировать набор тестовых данных в данном случае нет никакого смысла.

train_dt <- indctrs1[date < as.Date("2022-01-01")]
predict_dt <- indctrs1[date >= as.Date("2021-12-01")]

Будет рассматриваться две простых модели:

  1. rec_glm1 – будет включать все предикторы
  2. rec_glm2 – будет включать лишь предикторы, которые характеризуют относительную динамику
rec_glm1 rec_glm2
(Intercept) -3.340 -2.163***
date -0.001
PAYEMS -0.015***
INDPRO -1.073
W875RX1 -0.023***
CMRMTSPL 0.000*
PAYEMS_ch -267.642** 193.241***
INDPRO_ch -58.986 -98.879***
W875RX1_ch -155.574+ -83.042**
CMRMTSPL_ch -48.484 -62.719***
PAYEMSlg 0.015***
INDPROlg 1.812+
W875RX1lg 0.023***
CMRMTSPLlg 0.000
PAYEMS_chlg -70.689 104.529***
INDPRO_chlg -12.577 -72.508**
W875RX1_chlg -67.249 -48.216*
CMRMTSPL_chlg -46.000 -68.095***
Num.Obs. 644 644
AIC 135.1 370.9
BIC 215.5 411.1
Log.Lik. -49.539 -176.464
RMSE 0.14 0.28

Показатели AIC и BIC красноречиво говорят, что первая модель существенно лучше второй, но не будем торопиться с выводами. Модель rec_glm1 считает, что предикторы PAYEMS, W875RX1 – являются значимыми, пометив их ***, и также они имеют отрицательные коэффициенты модели. На самом деле – это не очень хорошо т.к. два этих показателя – являются растущими на протяжение всего исследуемого отрезка времени. С учетом отрицательных коэффициентов получается, что по мере роста этих предикторов вероятность рецессии будет снижаться, но предикторы будут расти постоянно хотя бы из-за инфляции. Таким образом, модель показывает сильную регрессионную зависимость вероятности рецессии на тренд, о чем можно самостоятельно убедиться, построив такую простую регрессию.

В действительности неправильно делать прогнозы о том, что дождь не пойдет завтра только на основании того факта, что дождя не было всю неделю. Это так не работает и точно такая я же ситуация с прогнозированием рецессии: нельзя делать вывод, что рецессий будет меньше в будущем только на основании факта, что их было мало в последнее время. Скорее наоборот – следует прогнозировать рецессии как Пуассоновский процесс путем поиска параметров распределения интенсивности появления рецессий. В этом случае более длительный период с момента прошлой рецессии будет повышать вероятность наступления новой рецессии. Кроме того, кризисы бывают циклические, а бывают системные и второй тип кризиса – это совершенно иная история с бифуркациями, турбулентностью и “толстым” хвостом в отрицательной области в распределении движений фондового рынка.

Поэтому, несмотря на более высокую точность, далее я буду отдавать предпочтение модели rec_glm2, которая фактически ловит вибрации экономических настроений и конвертирует их в прогноз наличия рецессии. Любопытно, что rec_glm2 принимает значимыми все предикторы, которые включены в модель. Напомню, что предикторы представляют собой относительные изменения и следовательно коэффициенты модели можно сравнивать в лоб не только для определения значимости, но и для определения существенности. Наиболее существенным ~193 коэффициентом обладает PAYEMS_ch, который также является положительным тогда как INDPRO_ch, W875RX1_ch, CMRMTSPL_ch имеют отрицательный знак и совокупное значение ~245. Ситуация для сдвинутых на период значений выглядит аналогичным образом. Получается, что вероятность рецессии сильно повышается тогда когда растут доходы населения, но падают прочие индикаторы

Далее хотелось бы посмотреть насколько выполнено условие экзогенности для выбранных предикторов для чего остатки модели будут проверены на коррелированность с предикторами:

# Таблица с модельными данными 
fitted_dt <- tfm(train_dt, fit = rec_glm2$fitted.values) |>
  tfm(resid = fit - USRECD, # остатки модели 
      pred = fifelse(fit > .5, 1, 0)) # прогноз 

# Проверка корреляции остатков
cor1 <- fitted_dt |>
  get_vars(vars = "_ch|resid", regex =  TRUE) |> 
  cor() |> 
  dapply(\(x)round(x, 3)) 
  
emphatic::hl_mat(cor1[c(1:4, 9), c(1:4, 9)], scale_color_fira(continuous = TRUE)) |> 
  as_html(style = "color:White;font-size:15px")
              PAYEMS_ch INDPRO_ch W875RX1_ch CMRMTSPL_ch resid
PAYEMS_ch         1.000     0.750      0.479       0.588 0.000
INDPRO_ch         0.750     1.000      0.475       0.700 0.000
W875RX1_ch        0.479     0.475      1.000       0.387 0.000
CMRMTSPL_ch       0.588     0.700      0.387       1.000 0.000
resid             0.000     0.000      0.000       0.000 1.000

И вторая группа предикторов, сдвинутых на один период:

emphatic::hl_mat(cor1[5:9, 5:9], scale_color_fira(continuous = TRUE)) |> 
  as_html(style = "color:White;font-size:15px")
                PAYEMS_chlg INDPRO_chlg W875RX1_chlg CMRMTSPL_chlg resid
PAYEMS_chlg           1.000       0.750        0.480         0.588 0.000
INDPRO_chlg           0.750       1.000        0.475         0.700 0.000
W875RX1_chlg          0.480       0.475        1.000         0.387 0.000
CMRMTSPL_chlg         0.588       0.700        0.387         1.000 0.000
resid                 0.000       0.000        0.000         0.000 1.000

Корреляции с остатками resid, что означает отсутствие эндогенности и следовательно в каком-то смысле полноту выбранного набора предикторов, а также состоятельность заключений, который можно сделать на основе данной модели.

Также интересно посмотреть как ошибки модели rec_glm2 ведут себя с течением времени: нет ли там каких-то паттернов и закономерностей.

fitted_dt |> 
  e_chart(date) |> 
  e_line(resid, itemStyle  = list(opacity = 0.7), symbol= 'none', color = my_pal[1], name = "Ошибка") |>
  e_area(USRECD, symbol= 'none', lineStyle = list(width = 0), name = "Период рецессии", 
         color = "rgba(225, 87, 89, .5)") |>
  e_tooltip(trigger = "axis", formatter = e_tooltip_pointer_formatter("decimal", digits = 2)) |> 
  e_datazoom(type = "slider", xAxisIndex = "all") |>
  e_y_axis(name = "Ошибки регресии") |>
  e_draft("InvestCookies.ru", size = "80px", opacity = 0.2)

Модель становится неуверенной в периоды рецессий и также в ближайших к ним окрестностях. Складывается впечатление, что экономическая система как бы работает в двух режимах: один более свойственен спокойному периоду роста и другой более закономерен для периода рецессии. Вероятно, именно эта идея легла в основу Markov Switching Models – Марковская модель с переключением, которую использовала Marcelle Chauvet для прогноза, который публикует ФРС. Подробно разбор данной модели будет в следующей заметки, а пока необходимо сделать прогноз на базе модели простой логистической регрессии:

# Функция которая считает полезные метрики модели
get_metrics <- function(dt){
  mtrx <- table(dt)
  n <- fnrow(dt)
  accuracy <- round(fsum(diag(mtrx))/n, 2)
  precision = round(diag(mtrx) / colSums(mtrx) , 2)
  recall = round(diag(mtrx) / rowSums(mtrx), 2)
  f1 = round(2 * precision * recall / (precision + recall) , 2)
  
  qDF(mtrx) |> cbind(data.frame(metrics = c("->", "->"), precision, recall, f1)) |> 
    hl(scale_color_fira(continuous = TRUE), cols = 1:2, calc_scale = "each") |>
    as_html(style = "color:White")
}

fselect(fitted_dt, actual = USRECD, prediction = pred)|> 
  get_metrics()
      0  1 metrics precision recall   f1
0   550  9      ->      0.91   0.98 0.94
1    54 31      ->      0.78   0.36 0.49

Цветной блок слева показывает какое количество периодов рецессии было предсказано верно, а какой – не верно и также показывает ложноположительные и ложноотрицательные ошибки в виде матрицы. Модель смогла верно предсказать 31 период и прошляпить 54 периода и также 9 раз предсказать рецессию, которой на самом деле не было. Другими словами, модель слишком слабо откликается на возможную вероятность рецессии, а именно эта характеристика – является важной.

Если немного поиграться с уровнем отсечения вероятности для отнесения периода к спаду, то можно получить сбалансированный результат на уровне вероятности 26%

tfm(train_dt, fit = rec_glm2$fitted.values) |>
  tfm(resid = fit - USRECD,
      pred = fifelse(fit > .26, 1, 0)) |> # предельное значения для определения рецессии = 26%
  fselect(actual = USRECD, prediction = pred)|> 
  get_metrics()
      0  1 metrics precision recall   f1
0   523 36      ->      0.93   0.94 0.93
1    37 48      ->      0.57   0.56 0.56

Теперь осталось сделать прогноз для 6 месяцев 2022 года:

res_dt <- tfm(indctrs1, mdl = predict(rec_glm2, newdata = indctrs1, type = "response")) 

res_dt |> 
  tfm(n = 1) |>
  tfm(n = cumsum(n)) |>
  e_chart(date) |> 
  e_line(mdl, itemStyle  = list(opacity = 0.7), #lineStyle = list(type = "dotted"),
         symbol= 'none', color = my_pal[3], name = "Модель") |>
  e_area(USRECD, symbol= 'none', lineStyle = list(width = 0), name = "Период рецессии",
         color = "rgba(225, 87, 89, .5)") |>
  e_loess(mdl ~ n, lineStyle = list(type = "dashed", width = 3), 
          emphasis = list(focus = 'self'), showSymbol = FALSE, color = my_pal[1], name = "Тренд модели") |>
  e_tooltip(trigger = "axis", formatter = e_tooltip_pointer_formatter("decimal", digits = 2)) |> 
  e_y_axis(name = "Ошибки регресии") |>
  e_datazoom(type = "slider", xAxisIndex = "all", start = 80) |>
  e_draft("InvestCookies.ru", size = "80px", opacity = 0.2) |> 
  e_mark_area(data = list(list(xAxis = as.Date("2022-01-01"), name = "Прогноз"), list(xAxis = as.Date("2022-07-01"))), 
              itemStyle = list(color = my_pal[1], opacity = .2))

Согласно не очень точной, но обоснованной модели два месяца из шести находились в состоянии рецессии, хотя скорее данный период можно назвать терминальным, когда рецессия как бы есть и ее как бы нет, что вполне в духе моей предыдущей заметки. Также любопытно, что тренд прогноза модели находится на исторических максимумах, превышающих пики 70-х годов, что вероятно говорит о переходе экономической системы в новую нормальность повышенных рисков рецессии.

Выводы 🍪

  • Выбранные предикторы: Занятость, Доходы населения, Производство, Продажи, – неплохо согласуются с формулой расчета ВВП по добавленной стоимости и также не содержат признаков эндогенности
  • Прогнозировать отсутствие рецессии исходя из простого факта, что ее долго не было – есть форменная глупость
  • Простая модель логистической рецессии говорит, что последние 6 месяцев в США со статусом рецессии по-прежнему все не ясно
  • Необходима более совершенная модель, например, Markov Switching Models, которая будет подробна разобрана в следующей заметке
  • Судя по росту вероятности рецессии в последнее время – наступает период новой нормальности, характеризующаяся повышенными рисками рецессии для США и всей мировой экономики

Простой способ узнать о новых публикациях – подписаться на Telegram-канал:


  1. Smoothed U.S. Recession Probabilities ↩︎

  2. AN ECONOMETRIC CHARACTERIZATION OF BUSINESS CYCLE DYNAMICS WITH FACTOR STRUCTURE AND REGIME SWITCHING ↩︎