Глава 146: Физически-информированные нейронные сети для модели CIR
Обзор
Модель Кокса-Ингерсолла-Росса (CIR) — одна из наиболее важных моделей короткой ставки в количественных финансах. В отличие от модели Васичека, CIR гарантирует неотрицательность процентных ставок — критически важное свойство для реалистичного моделирования. В этой главе мы строим физически-информированную нейронную сеть (PINN), которая решает PDE ценообразования облигаций CIR, калибруется на рыночных данных (включая ставки фондирования криптовалют с биржи Bybit) и применяет модель CIR к моделированию кредитного риска.
Модель CIR описывает эволюцию короткой ставки r(t) через стохастическое дифференциальное уравнение:
dr = kappa * (theta - r) * dt + sigma * sqrt(r) * dWгде:
- kappa > 0 — скорость возврата к среднему (насколько быстро ставки притягиваются к долгосрочному среднему)
- theta > 0 — долгосрочное среднее (равновесная ставка)
- sigma > 0 — коэффициент волатильности
- sqrt(r) делает волатильность пропорциональной корню из текущей ставки
- dW — стандартный винеровский процесс (броуновское движение)
Ключевое наблюдение: множитель sqrt(r) обеспечивает то, что при приближении ставок к нулю волатильность исчезает, предотвращая отрицательные ставки. Это гарантируется при выполнении условия Феллера:
2 * kappa * theta >= sigma^2Когда это условие выполнено, процесс ставки никогда не достигает нуля.
Почему CIR лучше других моделей короткой ставки?
Сравнение моделей
| Характеристика | Васичек | Халл-Уайт | CIR |
|---|---|---|---|
| СДУ | dr = kappa(theta-r)dt + sigma dW | dr = [theta(t)-ar]dt + sigma dW | dr = kappa(theta-r)dt + sigma*sqrt(r) dW |
| Отрицательные ставки | Возможны | Возможны | Невозможны (условие Феллера) |
| Волатильность | Постоянная | Постоянная или зависит от t | Зависит от ставки: sigma*sqrt(r) |
| Аналитические цены облигаций | Да | Да | Да (через функции Бесселя) |
| Плотность перехода | Нормальная | Нормальная | Нецентральная хи-квадрат |
| Сложность калибровки | Низкая | Средняя | Средняя |
| Реалистичность для крипто | Нет | Частично | Да (ставки всегда >= 0) |
Почему CIR для криптовалютных рынков?
На криптовалютных рынках несколько “процентно-ставочных” величин естественно неотрицательны:
- Ставки фондирования: ставки фондирования бессрочных фьючерсов на биржах вроде Bybit
- Ставки DeFi-кредитования: Aave, Compound — всегда неотрицательны
- Доходность стейкинга: доходность стейкинга Ethereum строго положительна
- Возврат к среднему подразумеваемой волатильности: IV криптовалют демонстрирует возврат к среднему с зависящей от уровня волатильностью
Модель CIR естественно описывает эту динамику.
PDE CIR для ценообразования облигаций
Вывод
Бескупонная облигация с погашением T выплачивает $1 в момент T. Её цена P(r, t) зависит от текущей короткой ставки r и времени t. Используя ценообразование в риск-нейтральной мере и лемму Ито, P удовлетворяет PDE CIR для ценообразования облигаций:
dP/dt + kappa * (theta - r) * dP/dr + 0.5 * sigma^2 * r * d^2P/dr^2 - r * P = 0с терминальным условием:
P(r, T) = 1 для всех r >= 0Ключевое отличие от PDE Халл-Уайта: обратите внимание на множитель r при второй производной (0.5 * sigma^2 * r * d^2P/dr^2). В модели Халл-Уайта этот коэффициент равен 0.5 * sigma^2 * d^2P/dr^2 (без множителя r). Этот зависящий от r коэффициент диффузии делает PDE CIR более сложным численно — особенно вблизи r = 0, где коэффициент диффузии обращается в ноль.
Граничные условия
При r = 0: dP/dt + kappa * theta * dP/dr = 0 (поглощающая/отражающая граница)
При r -> бесконечность: P(r, t) -> 0 (высокие ставки обесценивают облигации)
При t = T: P(r, T) = 1 (терминальная выплата)Аналитическое решение
Модель CIR имеет элегантную замкнутую формулу цены облигации:
P(r, t) = A(tau) * exp(-B(tau) * r)
где tau = T - t (время до погашения)
gamma = sqrt(kappa^2 + 2 * sigma^2)
B(tau) = 2 * (exp(gamma * tau) - 1) / ((gamma + kappa) * (exp(gamma * tau) - 1) + 2 * gamma)
A(tau) = [ 2 * gamma * exp((kappa + gamma) * tau / 2) / ((gamma + kappa) * (exp(gamma * tau) - 1) + 2 * gamma) ] ^ (2 * kappa * theta / sigma^2)Эта аналитическая формула служит эталоном для валидации нашей PINN.
Доходность и форвардная ставка
Из цены облигации выводим:
Доходность: y(r, tau) = -ln(P(r, t)) / tau = (B(tau) * r - ln(A(tau))) / tau
Мгновенная форвардная ставка: f(r, t, T) = -d ln(P) / dT = B'(tau) * r - A'(tau) / A(tau)Плотность перехода: нецентральное хи-квадрат распределение
Процесс CIR имеет известную плотность перехода. При заданном r(s) = r_s распределение r(t) для t > s:
r(t) | r(s) = r_s ~ (sigma^2 * (1 - exp(-kappa*(t-s)))) / (4*kappa) * chi^2(df, lambda)
где: df = 4 * kappa * theta / sigma^2 (число степеней свободы) lambda = 4 * kappa * r_s * exp(-kappa*(t-s)) / (sigma^2 * (1 - exp(-kappa*(t-s)))) (параметр нецентральности)Это нецентральное хи-квадрат распределение критически важно для:
- Оценки максимального правдоподобия (MLE) параметров CIR
- Точного моделирования траекторий CIR (без ошибки дискретизации)
- Аналитического вычисления цен опционов на облигации
Архитектура PINN для CIR
Проектирование сети
PINN принимает короткую ставку r и время t на вход и выдаёт цену облигации P(r, t):
Архитектура PINN для CIR========================================
ВХОДНОЙ СЛОЙ +---------------------------+ | (r, t) | 2 нейрона | r = короткая ставка | | t = текущее время | +---------------------------+ | v ПРЕОБРАЗОВАНИЕ ВХОДА +---------------------------+ | r_norm = r / r_max | Нормализация к [0, 1] | t_norm = t / T | | sqrt_r = sqrt(r) / sqrt(r_max) <-- учёт структуры sqrt(r) +---------------------------+ | v СКРЫТЫЕ СЛОИ +---------------------------+ | Слой 1: Linear(3, 128) | 3 входа: r_norm, t_norm, sqrt_r | + Tanh | +---------------------------+ | v +---------------------------+ | Слой 2: Linear(128, 128) | | + Tanh | +---------------------------+ | v +---------------------------+ | Слой 3: Linear(128, 128) | | + Tanh | +---------------------------+ | v +---------------------------+ | Слой 4: Linear(128, 64) | | + Tanh | +---------------------------+ | v ВЫХОДНОЙ СЛОЙ +---------------------------+ | Слой 5: Linear(64, 1) | | + Sigmoid | Гарантирует P в (0, 1) +---------------------------+Зачем sqrt(r) как входной признак? PDE CIR имеет сингулярную структуру sqrt(r). Предоставляя sqrt(r) напрямую, сеть не должна обучать это преобразование внутри себя, что значительно улучшает сходимость вблизи r = 0.
Зачем Sigmoid на выходе? Цены бескупонных облигаций всегда в (0, 1], поэтому сигмоидная активация на выходном слое естественно обеспечивает это ограничение.
Обработка сингулярности sqrt(r)
Вблизи r = 0 PDE CIR становится вырожденным: коэффициент диффузии 0.5 * sigma^2 * r обращается в ноль. Это создаёт численные сложности для PINN:
- Остаток PDE доминируется членом сноса kappa * theta * dP/dr
- Малые ошибки в d^2P/dr^2 усиливаются относительно исчезающей диффузии
- Стандартная равномерная коллокация недостаточно покрывает критическую область вблизи нуля
Реализованные решения:
# 1. Коллокация с учётом sqrt(r): больше точек вблизи r = 0r_collocation = r_max * (torch.rand(N) ** 2) # квадратичная выборка
# 2. Взвешенная потеря PDE: больший вес остаткам вблизи r = 0weights = 1.0 / (r_collocation + epsilon)
# 3. Вспомогательный вход sqrt(r): подаём sqrt(r) как признакinputs = torch.stack([r, t, torch.sqrt(r + epsilon)], dim=-1)
# 4. Регуляризованная диффузия: добавляем epsilondiffusion = 0.5 * sigma**2 * (r + epsilon)Проектирование функции потерь
Общая потеря для обучения CIR PINN:
L_total = w_pde * L_pde + w_bc * L_bc + w_ic * L_ic + w_analytical * L_analytical1. Потеря остатка PDE
Выбираем коллокационные точки (r_i, t_i) во внутренней области и вычисляем:
P = network(r, t)
# Автоматическое дифференцированиеdP_dt = autograd(P, t)dP_dr = autograd(P, r)d2P_dr2 = autograd(dP_dr, r)
# Остаток PDE CIRresidual = dP_dt + kappa * (theta - r) * dP_dr + 0.5 * sigma**2 * r * d2P_dr2 - r * P
L_pde = mean(weights * residual**2)2. Потеря граничных условий
# Терминальное условие: P(r, T) = 1P_terminal = network(r_terminal, T)L_ic = mean((P_terminal - 1.0)**2)
# Нижняя граница: при r = 0, редуцированное PDE# Верхняя граница: P -> 0 при r -> бесконечность3. Потеря аналитических ограничений (опционально)
Используем небольшой набор аналитически вычисленных цен CIR как супервизию.
Стратегия обучения
Фаза 1: Тёплый старт с аналитическими решениями (эпохи 1-500)
- Высокий вес L_analytical, низкий вес L_pde
- Сеть обучает общую форму поверхности цен облигаций
Фаза 2: Обучение с фокусом на PDE (эпохи 501-3000)
- Увеличение веса L_pde, уменьшение L_analytical
- Сеть уточняет решение PDE
Фаза 3: Тонкая настройка (эпохи 3001-5000)
- Сбалансированные веса всех компонент потерь
- Адаптивная коллокация
Калибровка параметров CIR через MLE
Перед обучением PINN нужно определить параметры CIR (kappa, theta, sigma). Оценка максимального правдоподобия использует нецентральное хи-квадрат распределение перехода:
def cir_log_likelihood(params, rates, dt): kappa, theta, sigma = params c = 2 * kappa / (sigma**2 * (1 - np.exp(-kappa * dt))) df = 4 * kappa * theta / sigma**2
for i in range(1, len(rates)): noncentrality = 2 * c * rates[i-1] * np.exp(-kappa * dt) x = 2 * c * rates[i] ll += ncx2.logpdf(x, df=df, nc=noncentrality) + np.log(2 * c)
return -ll # минимизируем отрицательное логарифмическое правдоподобиеКредитный риск: CIR для интенсивности дефолта
Модель CIR широко используется для моделирования интенсивности дефолта (функции опасности) lambda(t) в кредитном риске:
d(lambda) = kappa * (theta - lambda) * dt + sigma * sqrt(lambda) * dWВероятность выживания (вероятность отсутствия дефолта до момента T):
Q(t, T) = E[exp(-integral_t^T lambda(s) ds) | lambda(t)]Замечательно, что она имеет точно такую же форму, как цена облигации CIR:
Q(t, T) = A(T-t) * exp(-B(T-t) * lambda(t))с теми же функциями A, B! Наша PINN может поэтому также оценивать:
- Кредитные дефолтные свопы (CDS)
- Облигации с кредитным риском
- Кредитные опционы
Применение к криптовалютным рынкам
Моделирование ставок фондирования Bybit
Бессрочные фьючерсы на Bybit имеют механизм фондирования каждые 8 часов. Ставка фондирования демонстрирует:
- Возврат к среднему: ставки возвращаются к базовому уровню
- Зависящую от уровня волатильность: более высокие ставки имеют более высокую волатильность
- Неотрицательность: абсолютная ставка |funding| всегда неотрицательна
Торговая стратегия: возврат ставки фондирования к среднему
def funding_rate_strategy(funding_rates, cir_params, threshold=2.0): kappa, theta, sigma = cir_params
for rate in funding_rates: expected_rate = theta + (rate - theta) * np.exp(-kappa * dt) rate_std = sigma * np.sqrt(rate * (1 - np.exp(-2*kappa*dt)) / (2*kappa)) z_score = (rate - theta) / max(rate_std, 1e-8)
if z_score > threshold: signal = -1 # Шорт: ставка вернётся вниз elif z_score < -threshold: signal = 1 # Лонг: ставка вернётся вверх else: signal = 0 # НейтральноУсловие Феллера: 2kappatheta >= sigma^2
Случай 1: 2*kappa*theta > sigma^2 (сильное условие Феллера) - r = 0 -- входная граница - Процесс НИКОГДА не достигает нуля
Случай 2: 2*kappa*theta = sigma^2 (критическое условие Феллера) - r = 0 достигается, но мгновенно отражается
Случай 3: 2*kappa*theta < sigma^2 (условие Феллера нарушено) - r = 0 достигается и процесс может застревать в нуле - Часто встречается в криптовалютах с высокой волатильностьюПримеры кода
Быстрый старт: Python
from python.cir_pinn import CIRPINNfrom python.train import train_pinnfrom python.analytical import cir_bond_price
# Параметры CIRkappa, theta, sigma = 0.5, 0.05, 0.1T = 5.0
# Создание и обучение PINNpinn = CIRPINN(kappa=kappa, theta=theta, sigma=sigma, T=T)losses = train_pinn(pinn, epochs=5000, lr=1e-3)
# Сравнение с аналитическим решениемr_test = torch.linspace(0.0, 0.15, 100)P_pinn = pinn.predict(r_test, torch.zeros(100))P_analytical = cir_bond_price(r_test.numpy(), 0.0, T, kappa, theta, sigma)Быстрый старт: Rust
use pinn_cir_model::{CIRParams, cir_bond_price, simulate_cir_paths};
fn main() { let params = CIRParams { kappa: 0.5, theta: 0.05, sigma: 0.1, r0: 0.03, };
let price = cir_bond_price(0.03, 5.0, ¶ms); println!("Цена облигации CIR (r=0.03, T=5): {:.6}", price);}Структура файлов
146_pinn_cir_model/|-- README.md # Английская версия|-- README.ru.md # Этот файл (русская версия)|-- readme.simple.md # Простое объяснение (англ.)|-- readme.simple.ru.md # Простое объяснение (рус.)|-- python/| |-- __init__.py # Инициализация пакета| |-- requirements.txt # Зависимости Python| |-- cir_pinn.py # Архитектура PINN для CIR PDE| |-- train.py # Цикл обучения с коллокацией sqrt(r)| |-- data_loader.py # Данные: казначейские + фондирование Bybit| |-- analytical.py # Аналитическое ценообразование CIR| |-- calibration.py # MLE-калибровка параметров CIR| |-- credit_risk.py # CIR для моделирования интенсивности дефолта| |-- visualize.py # Графики: траектории, поверхности, кривые| |-- backtest.py # Бэктестинг торговой стратегии|-- rust_pinn_cir/| |-- Cargo.toml # Конфигурация Rust проекта| |-- src/| | |-- lib.rs # Основная библиотека CIR| | |-- bin/| | |-- train.rs # Обучение PINN| | |-- price_bonds.rs # Ценообразование облигаций| | |-- fetch_data.rs # Загрузка данных Bybit| |-- examples/| |-- basic_pricing.rs # Простой пример ценообразованияЛитература
- Cox, J.C., Ingersoll, J.E., Ross, S.A. (1985). “A Theory of the Term Structure of Interest Rates.” Econometrica, 53(2), 385-407.
- Feller, W. (1951). “Two Singular Diffusion Problems.” Annals of Mathematics, 54(1), 173-182.
- Raissi, M., Perdikaris, P., Karniadakis, G.E. (2019). “Physics-Informed Neural Networks.” Journal of Computational Physics, 378, 686-707.
- Brigo, D., Mercurio, F. (2006). Interest Rate Models - Theory and Practice. Springer.
- Duffie, D., Singleton, K.J. (2003). Credit Risk. Princeton University Press.
Запуск кода
Python
cd 146_pinn_cir_model/pythonpip install -r requirements.txt
# Обучение PINNpython -m python.train --epochs 5000 --kappa 0.5 --theta 0.05 --sigma 0.1
# Загрузка данных Bybit и калибровкаpython -m python.data_loader --symbol BTCUSDT --days 180
# Бэктестингpython -m python.backtest --symbol BTCUSDT --days 90Rust
cd 146_pinn_cir_model/rust_pinn_cir
# Сборкаcargo build --release
# Обучениеcargo run --release --bin train
# Ценообразование облигацийcargo run --release --bin price_bonds
# Загрузка данных Bybitcargo run --release --bin fetch_data -- --symbol BTCUSDT --days 180
# Запуск примераcargo run --release --example basic_pricing