Перейти к содержанию

Набор предложений и контрфактические подсказки (T17)

offers.py собирает вместо одного контрпредложения несколько вариантов (SPEC §10). Каждый вариант — один вызов quote из limits.py (T16), поэтому лимит и ставка в предложении посчитаны так же, как в досье (LIMITS.md).

generate_offers(request, cash_flow, score, route_for, as_of) -> OfferSet
  • cash_flow — из cash_flow_from_responses (T16), score — ScoreResult основной модели, никогда не претендента.
  • route_for(amount) -> Route — маршрут для суммы: route_for(grade, flags, data_quality) из routing.py (T18, ROUTING.md).
  • as_of — день решения: от него считаются valid_until и reapply_after.

OfferSet содержит всё, что движок (T19) кладёт в Decision: offers, counterfactuals, reasons, matches_request, reapply_after, и ещё routes — маршрут, по которому посчитана цена каждого предложения. Статус решения выбирает T19: есть предложения — offered, нет — declined или in_review по маршруту.

Продукт и обеспечение

  • Продукт — preferred_product из заявки, а если его нет, то продукт по цели (PURPOSE_PRODUCT): оборудование → инвестиционный кредит, кассовый разрыв → овердрафт, сезонный разрыв или контракт → кредитная линия, остальное → оборотный кредит.
  • Срок вне диапазона продукта приводится к ближайшей границе: у овердрафта и кредитной линии лимит всегда на 12 месяцев. Тогда предложение не совпадает с запросом.
  • Стандартное обеспечение (SPEC §3–4):
Продукт ООО ИП
Оборотный кредит поручительство собственника нет
Инвестиционный кредит залог оборудования + поручительство залог оборудования
Овердрафт, кредитная линия нет нет

ИП отвечает по долгу всем личным имуществом, поэтому поручительство ему не нужно. LGD берётся по лучшему обеспечению (pricing.lgd), так что у инвестиционного кредита ООО LGD = 0,35.

Варианты

Варианты перебираются в порядке SPEC §10. После удаления одинаковых остаются первые MAX_OFFERS = 3.

kind Что это Когда появляется
as_requested Запрошенные продукт, сумма и срок Лимит покрывает запрос
longer_term Запрошенная сумма на минимальном сроке больше запрошенного, при котором quote.amount == amount Запрос не проходит; не для овердрафта и кредитной линии
max_amount Запрошенный срок, максимальная сумма Запрос не проходит, но лимит больше 0
with_collateral Плюс гарантия Корпорации МСП Только если она даёт то, чего не даёт стандартное обеспечение: запрошенную сумму или ставку под потолком
other_product Продукт по цели заявки Клиент выбрал другой продукт, например оборотный кредит для кассового разрыва

Вариант можно предложить, если сумма больше 0, не ниже минимума продукта, а ставка вместе с комиссией гарантии не выше 30% (within_cap). Выше потолка риск не укладывается в цену: система пробует гарантию МСП, а если не помогает — вариантов нет (SPEC §9.2).

Гарантия МСП вдвое снижает LGD, но стоит 1% годовых сверх ставки. Если клиент и без неё получает то же самое, гарантия обходится ему дороже, поэтому без пользы её не предлагаем. Поручительство и залог уже входят в стандартное обеспечение.

matches_request = true только у as_requested со сроком из заявки.

Маршрут каждого предложения

Стоимость обработки зависит от маршрута, а маршрут — от суммы (матрица полномочий, ROUTING.md). Поэтому вариант считается по маршруту запрошенной суммы, а если итоговая сумма попала на другой маршрут — пересчитывается по нему. Если маршруты не сходятся за MAX_ROUTE_ITERATIONS, берётся более дорогой. Предложения одной заявки могут оказаться на разных маршрутах (OfferSet.routes). Как из них получить один Decision.route, решает T19.

Тексты для клиента

  • tradeoff — компромисс варианта: «Запрошенная сумма, но срок 17 мес. вместо 12», «Запрошенный срок 12 мес., сумма до 730 000 ₽». У гарантии МСП там же её комиссия, у другого продукта — его условия (транши овердрафта на 60 дней). У кредитной линии указана комиссия 1% годовых за неиспользованный лимит: её нет ни в ставке, ни в Offer.
  • reasons — почему сумма такая: определяющее ограничение лимита на запрошенном сроке (limit_cash_flow, limit_leverage, limit_collateral, limit_turnover, limit_product_max) с числами для сотрудника и простой фразой для клиента. Плюс rate_above_cap, если ставка выше потолка, и limit_below_product_minimum, если предложений нет из-за маленького лимита.
  • Без предложений reapply_after = as_of + 90 дней: за квартал по счёту накопится новая история.

Контрфактические подсказки

Подсказки считаются повторным запуском quote с изменённым CashFlow или заявкой, только для ограничения, которое определяет лимит на запрошенном сроке, и только если запрос не проходит.

Ограничение lever Подсказка
cash_flow monthly_debt_payments Платёжная нагрузка по текущим кредитам, при которой проходит запрошенная сумма («при платёжной нагрузке по текущим кредитам до 13 817 ₽/мес. на 12 месяцев будет доступно 600 000 ₽»). Если такой нагрузки нет (нужна отрицательная) — сумма при погашении текущих кредитов
leverage current_debt Текущий долг, при котором проходит запрошенная сумма
collateral down_payment Собственный взнос 15% цены оборудования, если он меньше

В примере decision_offered.json подсказка «без текущего кредита» — 890 тыс. ₽. Движок даёт 900 тыс. ₽: он пересчитывает ставку под новую сумму (20,61% вместо 20,75%), и при более низкой ставке лимит выше. Пример — образец формата, а не эталон (CONTRACTS.md).

Для уровня THIN подсказки по потоку нет: лимит по потоку не считается (SPEC §5.1). Подсказка «накопите историю по счёту» — задача экрана предложений (T28) по DataQuality.client_message.

Упрощения

  • # SIMPLIFICATION: политика предложений — константы в offers.py, а не в config/: не больше 3 предложений, срок действия 10 дней, повторное обращение через 90 дней, комиссия 1% за неиспользованный лимит кредитной линии, соответствие цели и продукта. Перенос в config/credit_policy.json — отдельный PR в общую папку (docs/v2/NOTES.md).
  • # MOCK: гарантия Корпорации МСП — множитель к LGD и фиксированная комиссия, без заявки в Корпорацию (LIMITS.md).
  • Срок повторного обращения после экономического отказа одинаковый для всех. В банке он зависел бы от причины: для THIN — когда наберётся история, для долговой нагрузки — когда закроется кредит.