- BrainTools - https://www.braintools.ru -

Стресс-тест, который отвечает на вопрос бизнеса «а что, если»

Стресс-тест, который отвечает на вопрос бизнеса «а что, если» - 1

Ровно 18 лет назад, 15 сентября 2008 года, обанкротился Lehman Brothers — крупнейшее банкротство в истории США. Банк не использовал стресс-тесты для коммерческой недвижимости, поэтому ответа на вопрос «а что, если офисы и торговые центры обесценятся» у него не было.

Сегодня стресс-тест доступен любой компании, а не только банку с отделом риска. «А что, если доставка вырастет на 3 дня», «конкурент снизит цену на 15%», «ставка по ипотеке поднимется на 3 пункта» — на каждый вопрос можно ответить числом, а не словами.

Далее — шаги, как посчитать на своих данных. Код движка и инструкция в опенсорсе.

Стресс-тест отвечает на вопросы «а что, если» цифрами. В первой статье [1] мой риск-движок MAMIR прогнал ипотечный кризис 2008 года на 26.5 млн реальных записей — на его стресс-тестах и покажу, как это работает.

Для примера возьму сценарий «ставка вырастет на 3 пункта» — скажем, с 7% до 10%. Прибавить эти 3 пункта можно двумя разными механиками, и ответы получатся разные.

Данные из примера

Freddie Mac Single-Family Loan-Level — открытая помесячная история ипотечных займов: просрочки, дефолты, продажи залога.

Данные официальные и доступны любому. На странице датасета [2] регистрируешься и качаешь sample-файлы, по паре на каждый год выдачи: выдача и помесячное обслуживание. Структура полей описана в официальном User Guide [3].

Сдвиг — прибавить три к среднему за год

Модель не читает всю историю займа. Она видит фичи — числа, посчитанные из истории. Среднюю ставку за год, сколько раз человек просрочил платеж. Сдвиг правит эти фичи напрямую.

Надо проверить рост ставки на три пункта — беру среднюю за год, прибавляю три, смотрю ответ. Сами данные не трогаю. Так делают, потому что дешево. Пара строк кода, ответ сразу.

// «ставка выросла на 3 пункта»
[{ feature: 'loan_rate_mean_365d', op: 'add', value: 3 }]

Правишь среднее — переписываешь прошлое

Модель видит не ставку по займу, а среднюю за последний год. Сейчас 7%. Чтобы спросить про рост до 10%, прибавляю 3 пункта. Но средняя за год будет 10%, только если ставка была такой все двенадцать месяцев.

Хотел спросить, что будет после повышения, а вместо этого сказал модели, что повышение случилось год назад и заемщик уже год платит по новой ставке.

Одно поправил, остальные фичи сломались

Из остатка долга у меня выходят три числа — среднее за год, минимальное за год и среднее по штату. Поправил среднее, а минимальное и штатное остались от старых данных. Три числа больше не сходятся между собой, и модель получает заемщика, которого не бывает.

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

Получается, тестировал несуществующий мир. Сдвиги выбросил и стал искать другую механику.

Шок — пересчитать все заново

Стресс-тест на шоках меняет сами данные, а все числа для модели пересчитываются заново. Ставка выросла на три пункта — значит, меняю ставку в каждой записи по займу. Дальше тот же код, что готовил данные для обучения [4], заново считает фичи, которые из этого поля выходят. Руками эти числа не трогаю, они выходят сами и сходятся между собой.

// «ставка выросла на 3 пункта»
[{ field: 'CurrentInterestRate', op: 'add', value: 3 }]

Разница со сдвигом — одно слово в конфиге, feature стало field. Правится не вход модели, а само поле в данных.

Цена

Сдвиг отвечал мгновенно, шоку надо пройти по 5.3 млн событий, ответ занимает около трех минут на M3 Max.

Ограничение — не все можно шокировать

Шокировать можно не любое поле. Ставку — да, ее задает банк или рынок, она приходит извне. А просрочку — нет. Платит человек или перестал — это и надо узнать. Скажу в сценарии «пусть у всех будет по три просрочки» — модель покажет большие потери. Только это не прогноз. Я вписал ответ в вопрос и получил его же обратно.

Сценарий «ставка выросла на 3 пункта»

Вопрос со ставкой записывается в движке так — это реальная декларация из plugins/credit-risk, шок из примера выше, только с адресом и именем.

{
	id: 'high_ltv_rate_shock',
	title: 'Rate rise for the high-LTV-at-origination cohort',
	select: [{ field: 'OriginalLTV', op: 'gte', value: 90 }],
	shock: [{ field: 'CurrentInterestRate', op: 'add', value: 3 }],
}

select выбирает, кого касается вопрос. Здесь это займы с LTV при выдаче от 90 — первый взнос меньше десятой части цены жилья, и рост ставки первым делом бьет по такому плечу. Пустой select задал бы тот же вопрос всему портфелю. shock двигает наблюдаемое поле события, операций две — add и mul. Кода под сценарий не пишут, движок про ипотеку ничего не знает.

Ставка в этих данных фиксированная, у выданного займа она от рынка не растет. Так что сценарий условный — вроде «всем пересчитали ставку на три пункта вверх». Механике все равно, какое поле двигать.

Прогон на реальных данных

Данные — Freddie Mac Single-Family Loan-Level Dataset [2], те же 26.5 млн записей, что в первой статье. Скачиваются бесплатно под собственный аккаунт, структура полей описана в официальном User Guide [3]. Лицензия запрещает перераспространение, поэтому в репозитории их нет. Нужны пары файлов когорт 1999–2007 в data/freddie-mac/sample_orig_*.txt с выдачей и sample_svcg_*.txt с помесячным обслуживанием.

./scripts/up.sh                # postgres в докере
./scripts/ingest.sh            # загрузка 26.5 млн событий, ~5 минут
npm run windows -w @mamir/backend -- --plugin credit_risk --what labels
npm run windows -w @mamir/backend -- --plugin credit_risk --what features
npm run dev -w @mamir/scoring &        # python-сайдкар с моделью
npm run backtest -w @mamir/backend -- --plugin credit_risk --from 2005-01-01 --steps 6
npm run dev -w @mamir/backend &        # ядро на :3001

curl -X POST localhost:3001/scenarios/high_ltv_rate_shock/run 
  -H 'content-type: application/json' 
  -d '{"plugin":"credit_risk","at":"2009-01-01T00:00:00.000Z","lookback":"400d"}'

at — момент, на который собирается портфель. lookback — сколько истории входит в позицию. Вся цепочка до первого сценария занимает около часа на M3 Max, самые дорогие шаги — расчет фич (638 секунд на 8 × 26.5 млн значений) и обучение с бэктестом (770 секунд).

Итог в деньгах

Кроме прибавки потерь в ответе два служебных блока, coverage и extrapolation. Прогон выше на портфеле 2009 года — 207 867 займов на 29.83 млрд долларов — дает такую картину.

  • deltaEL: ожидаемые потери выросли со 161.40 до 176.85 млн долларов — плюс 15.45 млн, рост на 9.6%.

  • coverage: пройдено 5 303 019 событий, отобрано и перезаписано 528 614. Шок дошел до данных. Ноль в перезаписанных при непустом отборе означал бы, что нужного поля в данных нет и нулевой эффект сценария ничего не доказывает — такой прогон движок помечает untouched вместо completed.

  • extrapolation: после шока средняя ставка за год у 7.9% займов уходит за 99-й перцентиль обучающей выборки, до шока таких было 0.077%. Обучающих примеров за этой границей — один из ста, ответ модели там ближе к догадке, чем к оценке.

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

Сам сценарий не проверишь — его будущее не наступает. Проверяется модель. Исторический прогон сравнивает предсказанные потери с реальными, на кризисе 2009 года разрыв был ×3.39, про это первая статья [1]. Стресс-тест не точнее модели, которую использует.

Свой вопрос

Чтобы прогнать свое «а что, если», нужно положить данные в движок и объявить сценарий из двух частей — select с условием, кто попадает под вопрос, и shock с полем и величиной. Вопрос «доставка вырастет на 3 дня» из начала статьи записался бы как shock: [{ field: 'delivery_days', op: 'add', value: 3 }]. Путь от пустой базы расписан в гайде репозитория mamir [5], синтетический домен можно поднять за 15 минут, ипотека Freddie Mac — около часа.

Какой вопрос «а что, если» вы бы прогнали первым?

Автор: nmrcs

Источник [6]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/35501

URLs in this post:

[1] первой статье: https://habr.com/ru/articles/1076714/

[2] странице датасета: https://www.freddiemac.com/research/datasets/sf-loanlevel-dataset

[3] User Guide: https://www.freddiemac.com/research/pdf/user_guide.pdf

[4] обучения: http://www.braintools.ru/article/5125

[5] гайде репозитория mamir: https://github.com/nmrcs/mamir/blob/main/docs/guide.md

[6] Источник: https://habr.com/ru/articles/1078332/?utm_campaign=1078332&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100