Библиотека признаков¶
Общий код расчёта признаков скоринговой карты (SPEC §8) для обучения (T10) и для движка на живых заявках. Обучение и движок обязаны считать признаки одним и тем же кодом: иначе модель учится на одних числах, а решения принимает по другим. Поэтому здесь же — правила по просрочкам (SPEC §5) и проверенная выручка, на которых стоят стоп-факторы и флаги (T14), и качество данных по счёту (T09, SPEC §5.1).
Как вызывать¶
from apps.api.features import (SourcePayloads, data_quality, data_quality_from_responses, extract_features,
features_from_responses)
# движок: ответы источников из обогащения (T07), на дату их получения
features = features_from_responses(enrichment.responses)
# обучение: копия ответов из синтетического мира, на дату среза мира
features = extract_features(SourcePayloads.from_company(company), world.meta.as_of)
# качество данных по счёту: ответы источников и заявленная клиентом выручка
quality = data_quality_from_responses(enrichment.responses, request.declared_annual_revenue)
quality = data_quality(SourcePayloads.from_company(company), company.declared_annual_revenue)
На вход идут только ответы источников в формате контрактов: ЕГРЮЛ / ЕГРИП, ФНС, счета и БКИ. Скрытая правда мира (q, архетип, исход) в библиотеку не попадает: from_company берёт у компании только эти четыре ответа. Если источник не ответил, его ответ — None, и все признаки, которые от него зависят, — null (интервал «нет данных» скоринговой карты, SPEC §5.1).
as_of — дата, на которую считаются возраст бизнеса, давность просрочек и окно запросов КИ. У движка это день, когда ответили источники (snapshot_date), у обучения — дата среза мира.
Признаки¶
Значения округляются: доли и отношения — до трёх знаков, дни остатка — до одного. Округление — часть определения признака: обучение видит те же числа, что и движок.
| Признак | Как считается | null, если |
|---|---|---|
inflow_trend_12m |
Поступления от покупателей за последние 6 месяцев / за предыдущие 6 − 1 | история по счёту короче 12 месяцев или в первом полугодии поступлений не было |
inflow_cv_12m |
Коэффициент вариации месячных поступлений от покупателей (стандартное отклонение по генеральной совокупности / среднее) | история короче 6 месяцев или поступлений не было |
months_without_inflows_12m |
Месяцев без поступлений от покупателей, в пересчёте на 12 месяцев | история короче 6 месяцев |
customers_count_12m, top_customer_share |
Из сводки по контрагентам за 12 месяцев | история короче 6 месяцев; доля — ещё и если покупателей нет |
cash_withdrawal_share |
Снятие наличных / все списания | история короче 6 месяцев или списаний не было |
balance_days_of_expenses |
Средний остаток / (среднемесячные списания / 30) | то же |
tax_payment_months_12m, payroll_payment_months_12m |
Месяцев с налоговыми / зарплатными платежами, в пересчёте на 12 месяцев. У ИП без сотрудников зарплаты нет, это 0, а не «нет данных» | история короче 6 месяцев |
worst_delinquency_days_24m_company |
Худшая просрочка по отчёту компании после правил по просрочкам (ниже) | ИП; у компании нет отчёта или в нём нет ни одного продукта |
worst_delinquency_days_24m_owner |
То же по отчёту собственника (у ИП — предпринимателя) | в отчёте нет ни одного продукта: нет кредитной истории — не то же, что чистая |
bki_inquiries_3m |
Запросы КИ за 3 месяца до as_of, по всем отчётам |
БКИ не ответило |
debt_to_revenue |
Остаток долга бизнеса (отчёт компании у ООО, отчёт предпринимателя у ИП; без выданных гарантий) / годовая выручка ФНС, пересчитанная на полный год | нет отчётности в ФНС или выручка нулевая |
limit_utilization |
Использованная часть действующих возобновляемых лимитов (овердрафты, кредитные линии, карты) по всем отчётам | возобновляемых продуктов нет |
business_age_months |
Полных месяцев от регистрации до as_of |
ЕГРЮЛ не ответил |
okved_risk_group |
Группа риска основного ОКВЭД из config/features.json |
то же |
legal_form |
Из ЕГРЮЛ / ЕГРИП | то же |
owner_or_director_changed_6m |
Директор или учредитель (у ИП — только предприниматель) появился после регистрации и меньше 6 месяцев назад | то же |
margin |
Чистая прибыль / выручка из ГИР БО | ИП (в декларации только доход); нет отчётности; выручка нулевая |
negative_equity |
Капитал в ГИР БО меньше нуля | ИП; нет отчётности |
Все признаки по счёту смотрят на последние 12 полных месяцев или на всю историю, если она короче, но не меньше 6 месяцев: с меньшей историей данные — THIN (SPEC §5.1). Счётчики месяцев пересчитываются на 12, чтобы 8 месяцев с налоговыми платежами из 8 читались как 12. «Операционные поступления» — поступления от покупателей: переводы со своих счетов, полученные кредиты и взносы учредителя — не выручка.
Правила по просрочкам¶
credit.py, пороги — в config/features.json. Просрочка учитывается, только если она:
- длиннее 5 дней (до 5 дней — техническая задержка);
- не меньше 1 000 ₽ и не меньше 1% платежа по графику.
Давность эпизода считается от пропущенного платежа (started_at). Эпизоды старше 24 месяцев идут с половинным весом, старше 36 — не учитываются. Текущая просрочка проходит те же правила.
Признак worst_delinquency_days_* — худшая просрочка в днях с учётом веса: эпизод 25–36-месячной давности на 100 дней даёт 50. Поэтому «24m» в имени признака означает «полный вес за 24 месяца», а в признак попадают и эпизоды до 36 месяцев с половинным весом.
Для стоп-факторов (T14) subject_history отдаёт по каждому субъекту очищенные эпизоды, worst_days_24m (худшая просрочка с полным весом, для «просрочка 90+ за 24 месяца») и current_overdue_days (для «текущая просрочка больше 30 дней»). inquiries_since считает запросы КИ с нужной даты. Наша заявка не оставляет запроса в отчёте (T06), поэтому все посчитанные запросы — чужие.
Выручка¶
revenue.py, fns_revenue(fns, egrul, accounts). ФНС даёт выручку за последний сданный календарный год, а клиент заявляет выручку за последние 12 месяцев. Поэтому считаются три числа:
filed— как в ФНС;annualized— пересчёт на полный год, если компания зарегистрирована в отчётном году:filed / доля года, которую она существовала;estimate_12m—annualized, перенесённая на последние 12 месяцев по динамике поступлений от покупателей: поступления за 12 месяцев / среднемесячные поступления отчётного года × 12. Нужны 12 месяцев истории и хотя бы 6 месяцев отчётного года на счёте, иначе остаётсяannualized.
Кто что использует: debt_to_revenue делит на annualized (цифра отчётности); флаг расхождения заявленной выручки (T14) должен сравнивать с estimate_12m; покрытие выручки в качестве данных (T09) — делить на annualized.
На мире (зерно 7, 3000 компаний) заявленная выручка честных клиентов отличается больше чем на 30%:
| от выручки ФНС как есть | от estimate_12m |
|
|---|---|---|
| Отчётность за полный год | 21% | 7% |
| Компания зарегистрирована в отчётном году | 82% | 15% |
«Завышателей» оценка по-прежнему ловит примерно в 93% случаев. Часть оставшихся честных клиентов и правда ошибается в заявке больше чем на 30%: так заявляют 4% честных клиентов (T04).
Качество данных по счёту¶
data_quality.py, data_quality(payloads, declared_revenue) → контракт DataQuality. Четыре показателя SPEC §5.1, у каждого свой уровень; итоговый уровень — худший из четырёх.
| Показатель | Как считается |
|---|---|
history_months |
Месяцев в выписке по нашему счёту |
revenue_coverage |
Поступления от покупателей за последние 12 месяцев / годовая выручка. Выручка — fns_revenue(...).annualized (выручка ФНС, пересчитанная на полный год, раздел «Выручка»); если в ФНС её нет или ФНС не ответила — заявленная клиентом. Что взято, видно в revenue_basis (fns / declared). При истории короче 12 месяцев поступления пересчитываются на 12: короткая история и так снижает первый показатель, и покрытие не должно наказывать за неё второй раз |
operating_inflow_share |
Поступления от покупателей / все поступления за последние 12 месяцев. Переводы со своих счетов, полученные кредиты и взносы учредителя — не выручка |
regularity_months |
Месяцев с поступлениями от покупателей из последних 12, без пересчёта |
Показатель, который посчитать нельзя (нет выписки по счёту; нет выручки ни в ФНС, ни в заявке), получает null и уровень THIN: данных для решения нет. Без заявки — например, при пакетном предодобрении (T22) — declared_revenue не передаётся.
client_message — фраза для клиента по уровню и худшему показателю (первому из худших в порядке таблицы), например «Мы видим не все обороты вашего бизнеса: похоже, часть выручки проходит через другие банки. Выписка из другого банка поможет увеличить лимит.»
Пороги — config/data_quality.json, значения принадлежат экономическому блоку; сейчас там пороги SPEC §5.1. Версия файла попадает в DataQuality.policy_version. Признаки скоринговой карты от этих порогов не зависят, поэтому их правка не требует переобучения карты.
На мире (зерно 7, 3000 компаний) у обычных компаний 74% FULL, 19% PARTIAL и 7% THIN; у всего мира — 65%, 22% и 13%. Клиенты с недавно открытым счётом и с раздутыми своими переводами (T04) никогда не получают FULL, клиенты с основным счётом в другом банке — в 2% случаев. Около 4% обычных компаний — PARTIAL только из-за доли операционных поступлений: за последние 12 месяцев они получили кредит или взнос учредителя.
Политика: config/features.json¶
Пороги правил по просрочкам, окно запросов КИ (3 месяца), окно смены директора или собственника (6 месяцев, им же пользуются факты T07) и группы риска ОКВЭД — кредитная политика, значения принадлежат экономическому блоку. Группы риска ОКВЭД сейчас повторяют отраслевые поправки к дефолтности в config/world_outcomes.json: поправка ≤ 0 — low, 0,1–0,2 — medium, от 0,3 — high. Код из списка или его префикс по границе точки (41 покрывает 41.20) выбирается по самому длинному совпадению; ОКВЭД, которого нет в списках, получает okved_default_risk_group.
Версия файла — это и версия набора признаков (Features.feature_set_version). Её поднимают при любой правке определений здесь или значений в файле, после чего скоринговую карту надо переобучить.
Упрощения¶
| Упрощение | Как было бы в реальности |
|---|---|
# SIMPLIFICATION: выручка переносится на последние 12 месяцев по динамике нашего счёта, даже если часть бизнеса идёт через другие банки |
Квартальные декларации по НДС и УСН, выписки других банков |
| Группы риска ОКВЭД заданы для дюжины отраслей мира, остальные коды получают группу по умолчанию | Таблица по всему дереву ОКВЭД, откалиброванная по дефолтам портфеля банка |
| Долг к выручке — только долг бизнеса; личные кредиты собственника ООО не учитываются | Совокупная долговая нагрузка группы: компания, собственник, связанные компании |
# SIMPLIFICATION: пороги качества данных взяты из SPEC §5.1 экспертно |
Пороги, откалиброванные по портфелю банка: при каком покрытии и истории лимит по потоку ещё надёжен |
# SIMPLIFICATION: история по счёту — все месяцы выписки: в мире нет месяцев без операций |
Считать только месяцы, в которых по счёту были операции |
Тесты¶
tests/test_features.py:
- из примеров источников в
tests/fixtures/sources/получается ровноtests/fixtures/features.json; - каждое правило по просрочкам проверяется на границе;
- на сгенерированном мире у каждого признака есть значения,
null— там, где ожидается (ИП, короткая история); - признаки несут исход (волатильные поступления — больше дефолтов);
- оценка выручки снижает долю честных клиентов с расхождением больше 30%.
tests/test_data_quality.py:
- из примеров источников получаются показатели и уровень
tests/fixtures/data_quality_full.json; - каждый порог из
config/data_quality.jsonпроверяется на границе; - короткая история, свои переводы, низкое покрытие, выручка за неполный год, заявленная выручка вместо ФНС, нет выписки;
- доли уровней на мире совпадают с ожидаемыми (
apps/world/README.md).