Долгосрочное прогнозирование динамики облигаций с помощью ансамбля статистических моделей

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

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

Архитектура ансамбля статистических моделей

Ансамбль статистических моделей для временных рядов объединяет прогнозы нескольких базовых алгоритмов через взвешенное или простое усреднение. Такая архитектура снижает дисперсию ошибок и повышает устойчивость к выбросам в данных. Для прогнозирования доходности облигаций я буду использовать комбинацию трех моделей: AutoETS, AutoARIMA и SeasonalNaive. Каждая модель захватывает разные паттерны в временном ряде.

Компоненты ансамбля

AutoETS (Automatic Exponential Smoothing) автоматически выбирает оптимальную конфигурацию модели экспоненциального сглаживания из 30 возможных вариантов. Модель учитывает три компонента временного ряда:

  • Уровень (level);
  • Тренд (trend);
  • Сезонность (seasonality).

Параметр model=’ZZZ’ активирует автоматический подбор типа каждого компонента: аддитивный, мультипликативный или отсутствующий. Для доходности облигаций ETS эффективно захватывает плавные изменения уровня и краткосрочные колебания.

AutoARIMA автоматически определяет порядки авторегрессии (p), интегрирования (d) и скользящего среднего (q) через минимизацию информационного критерия Акаике (AIC). Модель подходит для временных рядов с явным трендом и автокорреляцией. В контексте облигаций ARIMA хорошо работает на периодах постепенного изменения процентных ставок, но может давать завышенные прогнозы при резких разворотах монетарной политики.

SeasonalNaive использует значения из предыдущего сезонного периода как прогноз для будущего. Для месячных данных с season_length=12 модель копирует значения годичной давности. Несмотря на простоту, SeasonalNaive служит надежным бейзлайном и защищает ансамбль от переобучения более сложных моделей. На рынке облигаций сезонность проявляется через налоговые циклы, квартальные аукционы Казначейства и паттерны ребалансировки портфелей институциональных инвесторов.

Метод агрегации прогнозов

Агрегация прогнозов в ансамбле реализуется через простое среднее арифметическое (Simple Average) с равными весами для каждой модели. Для трех компонентов это означает вес 1/3 для AutoETS, 1/3 для AutoARIMA и 1/3 для SeasonalNaive. Равновзвешенное усреднение не требует обучения мета-модели и защищает от переподгонки на исторических данных.

👉🏻  Градиентный бустинг: концепция и механизм работы

Можно экспериментировать с весами. Хотя исследования показывают, что простое усреднение часто превосходит сложные схемы взвешивания, особенно при ограниченном объеме обучающей выборки. Для временных рядов доходности облигаций длиной 5-10 лет оптимизация весов на валидационной выборке приводит к нестабильности из-за изменения режимов монетарной политики. Равные веса обеспечивают устойчивость прогноза к структурным сдвигам: если одна модель ошибается на резком развороте тренда, две другие компенсируют ее вклад.

Альтернативные методы агрегации включают:

  • Взвешивание по обратной ошибке на валидации (Inverse Error Weighting) — присваивает больший вес моделям с меньшей RMSE на тестовом окне, но требует дополнительного разбиения данных;
  • Стекинг с мета-регрессией — обучает линейную регрессию на прогнозах базовых моделей. Прогнозы могут стать точнее, однако растут риски переобучения в краткосрочных тенденциях.

Для доходности облигаций с частыми режимными сдвигами простое усреднение — оптимальный выбор баланса между точностью и устойчивостью.

Реализация на данных US Treasury 10Y

Реализация ансамбля статистических моделей для прогнозирования доходности 10-летних казначейских облигаций США включает три этапа: загрузку и предобработку исторических данных, обучение компонентов ансамбля и визуализацию результатов.

Я буду использовать библиотеку statsforecast — они заточена под быстрое и эффективное обучение моделей временных рядов и библиотеку yfinance для получения рыночных данных.

!pip install statsforecast

Подготовка данных

Исторические данные по доходности 10-летних облигаций США загружаются через тикер ^TNX (CBOE Interest Rate 10-Year T-Note). Данные содержат дневные значения доходности в процентах годовых. Для прогнозирования на месячном горизонте дневные данные агрегируются в месячные через среднее значение за месяц.

import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from statsforecast import StatsForecast
from statsforecast.models import AutoETS, AutoARIMA, SeasonalNaive
import warnings
warnings.filterwarnings('ignore')

# Загрузка данных по доходности 10-летних облигаций США
ticker = yf.Ticker("^TNX")
df_daily = ticker.history(period="10y")

# Проверка на MultiIndex
if isinstance(df_daily.columns, pd.MultiIndex):
    df_daily.columns = df_daily.columns.droplevel(1)

# Преобразование в месячные данные
df_daily['Date'] = df_daily.index
df_monthly = df_daily.resample('MS', on='Date').agg({'Close': 'mean'}).reset_index()
df_monthly.columns = ['Date', 'Yield']

# Удаление пропусков
df_monthly = df_monthly.dropna()

print(f"Загружено {len(df_monthly)} месячных наблюдений")
print(f"Период: {df_monthly['Date'].min()} - {df_monthly['Date'].max()}")
print(f"Доходность: min={df_monthly['Yield'].min():.2f}%, max={df_monthly['Yield'].max():.2f}%")

plt.figure(figsize=(12, 6))
plt.plot(df_monthly['Date'], df_monthly['Yield'])
plt.title("Доходность 10-летних облигаций США (месячные данные)")
plt.xlabel("Дата")
plt.ylabel("Доходность, %")
plt.grid(True)
plt.tight_layout()
plt.show()
Загружено 121 месячных наблюдений
Период: 2015-11-01 00:00:00-05:00 - 2025-11-01 00:00:00-05:00
Доходность: min=0.62%, max=4.80%

График доходности 10-летних облигаций США за период с 2015-2025, месячные данные

Рис. 1: График доходности 10-летних облигаций США за период с 2015-2025, месячные данные

Код загружает 10-летнюю историю дневных данных и агрегирует их в месячные через среднее значение доходности за месяц. Проверка на MultiIndex необходима из-за особенностей работы yfinance при загрузке нескольких тикеров.

👉🏻  Foundation-модели для временных рядов

Удаление пропусков обеспечивает непрерывность временного ряда для корректной работы моделей ARIMA и ETS. Результат: датафрейм с двумя столбцами Date (первый день месяца) и Yield (средняя доходность в процентах).

Обучение ансамбля

Функция forecast_bonds_ensemble реализует обучение ансамбля из 3-х статистических моделей на временном ряде доходности облигаций. Ключевые параметры: season_length=12 для захвата годовой сезонности в месячных данных и разделение на обучающую и тестовую выборки для валидации точности моделей.

def forecast_bonds_ensemble(df, target_column='Yield', periods=24, train_test_split=0.8):
    """
    Прогноз доходности облигаций на основе ансамбля трех моделей
    
    Parameters:
    -----------
    df : DataFrame
        Датафрейм с колонками Date и Yield
    target_column : str
        Название столбца с целевой переменной
    periods : int
        Количество месяцев для прогноза
    train_test_split : float
        Доля данных для обучения (остальное для валидации)
    
    Returns:
    --------
    DataFrame с историческими данными и прогнозом
    dict с метриками качества
    """
    
    df_forecast = df.copy()
    df_forecast['Date'] = pd.to_datetime(df_forecast['Date'])
    df_forecast = df_forecast.sort_values('Date').reset_index(drop=True)
    
    # Разделение на обучение и валидацию
    n_train = int(len(df_forecast) * train_test_split)
    df_train = df_forecast.iloc[:n_train].copy()
    df_test = df_forecast.iloc[n_train:].copy()
    
    print(f"Обучающая выборка: {len(df_train)} месяцев ({df_train['Date'].min()} - {df_train['Date'].max()})")
    print(f"Тестовая выборка: {len(df_test)} месяцев ({df_test['Date'].min()} - {df_test['Date'].max()})")
    
    # Подготовка данных для StatsForecast
    sf_data = pd.DataFrame({
        'unique_id': 'US_10Y',
        'ds': df_train['Date'],
        'y': df_train[target_column].values
    })
    
    # Создание ансамбля из трех моделей
    models = [
        AutoETS(season_length=12, model='ZZZ'),
        AutoARIMA(season_length=12),
        SeasonalNaive(season_length=12)
    ]
    
    model = StatsForecast(
        models=models,
        freq='MS',
        n_jobs=-1
    )
    
    # Обучение моделей
    print("\nОбучение моделей...")
    model.fit(sf_data)
    
    # Прогноз на горизонте валидации + будущего
    forecast_horizon = len(df_test) + periods
    forecast_df = model.predict(h=forecast_horizon)
    
    # Вычисление ансамблевого прогноза (простое среднее)
    forecast_df['Ensemble'] = forecast_df[['AutoETS', 'AutoARIMA', 'SeasonalNaive']].mean(axis=1)
    
    # Добавление дат прогноза
    last_train_date = df_train['Date'].max()
    forecast_dates = pd.date_range(
        start=last_train_date + pd.DateOffset(months=1),
        periods=forecast_horizon,
        freq='MS'
    )
    forecast_df['Date'] = forecast_dates
    
    # Разделение прогноза на валидационный и будущий периоды
    forecast_df['Type'] = ['Validation'] * len(df_test) + ['Forecast'] * periods
    
    # Объединение исторических данных и прогноза
    df_train['Type'] = 'Historical'
    df_train['AutoETS'] = df_train[target_column]
    df_train['AutoARIMA'] = df_train[target_column]
    df_train['SeasonalNaive'] = df_train[target_column]
    df_train['Ensemble'] = df_train[target_column]
    
    df_result = pd.concat([
        df_train[['Date', target_column, 'Type', 'AutoETS', 'AutoARIMA', 'SeasonalNaive', 'Ensemble']],
        forecast_df[['Date', 'Type', 'AutoETS', 'AutoARIMA', 'SeasonalNaive', 'Ensemble']].assign(**{target_column: None})
    ], ignore_index=True)
    
    # Вычисление метрик на валидационной выборке
    validation_mask = forecast_df['Type'] == 'Validation'
    y_true = df_test[target_column].values
    
    metrics = {}
    for model_name in ['AutoETS', 'AutoARIMA', 'SeasonalNaive', 'Ensemble']:
        y_pred = forecast_df.loc[validation_mask, model_name].values
        
        rmse = np.sqrt(np.mean((y_true - y_pred) ** 2))
        mae = np.mean(np.abs(y_true - y_pred))
        mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
        
        metrics[model_name] = {
            'RMSE': rmse,
            'MAE': mae,
            'MAPE': mape
        }
    
    print("\n=== Метрики на валидационной выборке ===")
    for model_name, model_metrics in metrics.items():
        print(f"{model_name:15} | RMSE: {model_metrics['RMSE']:.4f} | MAE: {model_metrics['MAE']:.4f} | MAPE: {model_metrics['MAPE']:.2f}%")
    
    return df_result, metrics


# Обучение ансамбля
import numpy as np
df_with_forecast, metrics = forecast_bonds_ensemble(
    df_monthly,
    target_column='Yield',
    periods=24,
    train_test_split=0.8
)

print(f"\nПрогноз завершен. Итоговый датафрейм: {len(df_with_forecast)} строк")
Обучающая выборка: 96 месяцев (2015-11-01 00:00:00-05:00 - 2023-10-01 00:00:00-05:00)
Тестовая выборка: 25 месяцев (2023-11-01 00:00:00-05:00 - 2025-11-01 00:00:00-05:00)

Обучение моделей...

=== Метрики на валидационной выборке ===
AutoETS         | RMSE: 1.1671 | MAE: 1.1374 | MAPE: 27.05%
AutoARIMA       | RMSE: 0.8558 | MAE: 0.8285 | MAPE: 19.78%
SeasonalNaive   | RMSE: 0.6429 | MAE: 0.5942 | MAPE: 13.86%
Ensemble        | RMSE: 0.6124 | MAE: 0.5283 | MAPE: 12.79%

Прогноз завершен. Итоговый датафрейм: 145 строк

Функция разделяет данные на обучающую (80%) и тестовую (20%) выборки для валидации точности моделей. Компоненты ансамбля настроены с season_length=12 для захвата годовой сезонности в доходности облигаций. Параметр freq=’MS’ указывает на месячную частоту данных с датами на начало месяца (Month Start). Обучение всех моделей происходит параллельно по всем ядрам CPU через n_jobs=-1.

👉🏻  Прогнозирование временных рядов с xLSTM

После обучения модели генерируют прогнозы на горизонте тестовой выборки плюс 24 месяца в будущее. Ансамблевый прогноз вычисляется как простое среднее трех базовых моделей. Функция возвращает датафрейм с историческими данными, валидационными и будущими прогнозами, а также словарь с метриками RMSE, MAE и MAPE для каждой модели и ансамбля.

Метрики вычисляются только на валидационной части, где известны фактические значения доходности:

  • RMSE (Root Mean Squared Error) измеряет среднеквадратичную ошибку прогноза;
  • MAE (Mean Absolute Error) показывает среднюю абсолютную ошибку прогноза;
  • MAPE (Mean Absolute Percentage Error) выражает ошибку в процентах от фактического значения доходности.

Визуализация результатов

Визуализация прогнозов ансамбля позволяет оценить поведение моделей на исторических данных и адекватность будущих прогнозов. Функция строит график с разделением на три зоны: исторические данные (черный), валидационный период (прогнозы vs факт) и будущие прогнозы (фиолетовый).

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

def plot_bond_forecast(df, target_column='Yield', save_path="bond_forecast.jpg"):
    """
    Визуализация прогноза доходности облигаций
    
    Parameters:
    -----------
    df : DataFrame
        Датафрейм с результатами прогнозирования
    target_column : str
        Название столбца с целевой переменной
    save_path : str
        Путь для сохранения графика
    """
    
    df_plot = df.copy()
    df_plot['Date'] = pd.to_datetime(df_plot['Date'])
    
    # Разделение на исторические данные, валидацию и прогноз
    historical = df_plot[df_plot['Type'] == 'Historical']
    validation = df_plot[df_plot['Type'] == 'Validation']
    forecast = df_plot[df_plot['Type'] == 'Forecast']
    
    fig, ax = plt.subplots(1, 1, figsize=(14, 6))
    
    # Исторические данные
    ax.plot(historical['Date'], historical[target_column],
            color='black', linewidth=1.5, label='Исторические данные')
    
    # Валидация: факт vs прогноз ансамбля
    if len(validation) > 0:
        # Фактические значения на валидации из исходного df_monthly
        validation_actual = df_monthly[
            (df_monthly['Date'] >= validation['Date'].min()) & 
            (df_monthly['Date'] <= validation['Date'].max()) ].sort_values('Date') # Соединительная линия от истории к валидации (прогноз) connect_dates = [historical['Date'].iloc[-1]] + validation['Date'].tolist() connect_ensemble = [historical['Ensemble'].iloc[-1]] + validation['Ensemble'].tolist() ax.plot(connect_dates, connect_ensemble, color='#A23B72', linewidth=1.5, alpha=0.7, label='Прогноз ансамбля (валидация)') # Фактические значения на валидации (темно-синяя линия) ax.plot(validation_actual['Date'], validation_actual[target_column], color='#00008B', linewidth=1.5, label='Факт (валидация)', zorder=5) # Будущий прогноз if len(forecast) > 0:
        last_date = validation['Date'].iloc[-1] if len(validation) > 0 else historical['Date'].iloc[-1]
        last_value = validation['Ensemble'].iloc[-1] if len(validation) > 0 else historical['Ensemble'].iloc[-1]
        
        connect_dates = [last_date] + forecast['Date'].tolist()
        connect_ensemble = [last_value] + forecast['Ensemble'].tolist()
        
        ax.plot(connect_dates, connect_ensemble,
                color='#A23B72', linewidth=2, label='Прогноз ансамбля (будущее)')
    
    # Разделительные линии
    if len(validation) > 0:
        ax.axvline(validation['Date'].iloc[0], color='gray', linestyle=':', linewidth=1, alpha=0.5)
    if len(forecast) > 0:
        ax.axvline(forecast['Date'].iloc[0], color='gray', linestyle='--', linewidth=1, alpha=0.5)
    
    ax.set_title('Прогноз доходности US Treasury 10Y: ансамбль', fontsize=12, pad=10)
    ax.set_ylabel('Доходность, %', fontsize=10)
    ax.set_xlabel('Дата', fontsize=10)
    ax.grid(True, alpha=0.3, linewidth=0.5)
    ax.legend(fontsize=9, loc='best')
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
    ax.xaxis.set_major_locator(mdates.YearLocator())
    plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
    
    plt.tight_layout()
    
    fig.savefig(save_path, dpi=150, format="jpg", bbox_inches="tight")
    plt.show()
    
    print(f"График сохранен: {save_path}")


# Построение графиков
plot_bond_forecast(
    df_with_forecast,
    target_column='Yield',
    save_path="bond_yield_forecast.jpg"
)

Прогноз доходности 10-летних казначейских облигаций США. График демонстрирует качество работы ансамбля на исторических данных, валидационном периоде и будущем прогнозе. Сопоставление темно-синей линии (факт) с фиолетовой (прогноз ансамбля) на валидационном участке позволяет визуально оценить точность модели. Продолжение фиолетовой линии вправо показывает прогноз на 24 месяца вперед с сохранением выявленных тенденций

Рис. 2: Прогноз доходности 10-летних казначейских облигаций США. График демонстрирует качество работы ансамбля на исторических данных, валидационном периоде и будущем прогнозе. Сопоставление темно-синей линии (факт) с фиолетовой (прогноз ансамбля) на валидационном участке позволяет визуально оценить точность модели. Продолжение фиолетовой линии вправо показывает прогноз на 24 месяца вперед с сохранением выявленных тенденций

Оценка качества прогнозов

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

👉🏻  Нейросети для прогнозирования последовательных данных

Сравнение ансамбля с отдельными моделями на валидационной выборке показывает снижение ошибок прогноза через усреднение базовых компонентов:

  • AutoETS: RMSE = 1.17%, MAE = 1.14%, MAPE = 27.05%
  • AutoARIMA: RMSE = 0.86%, MAE = 0.83%, MAPE = 19.78%
  • SeasonalNaive: RMSE = 0.64%, MAE = 0.59%, MAPE = 13.86%
  • Ensemble: RMSE = 0.61%, MAE = 0.53%, MAPE = 12.79%

Ансамбль снижает RMSE на 5% относительно лучшей отдельной модели (SeasonalNaive) и на 29% относительно худшей (AutoETS). MAPE ансамбля на 1.07 процентных пункта ниже SeasonalNaive и на 6.99 процентных пункта ниже AutoARIMA. MAE улучшается на 11% по сравнению с SeasonalNaive.

SeasonalNaive неожиданно демонстрирует лучшие результаты среди отдельных моделей на валидационной выборке. Это указывает на наличие устойчивой годовой сезонности в доходности 10-летних казначейских облигаций США, которую сложные модели (AutoETS, AutoARIMA) переусложняют через подгонку под краткосрочные флуктуации. Простое копирование значений годичной давности оказывается более надежным базовым прогнозом, чем авторегрессионные и экспоненциальные модели.

Ансамблевое усреднение улучшает даже лучшую базовую модель за счет компенсации ошибок на разных участках временного ряда. AutoARIMA переоценивает резкие изменения доходности, AutoETS недооценивает долгосрочные тренды, SeasonalNaive игнорирует отклонения от годового цикла. Комбинация трех подходов с равными весами балансирует эти эффекты и снижает дисперсию ошибок на 5-11% относительно отдельных моделей.

Ограничения подхода и направления улучшений

Ансамбль статистических моделей хорошо подходит для прогнозирования доходности облигаций на горизонте 12–24 месяцев, особенно в условиях плавных изменений процентных ставок. Однако у подхода есть ограничения: он хуже справляется со структурными сдвигами в монетарной политике и не учитывает влияние ключевых макроэкономических факторов. Повысить точность и устойчивость таких прогнозов можно за счет расширения ансамбля — например, добавив модели машинного обучения и экзогенные признаки.

👉🏻  Прогнозирование вероятности дефолта через логистическую регрессию

Варианты расширения ансамбля

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

Так, модели LightGBM и XGBoost эффективны для прогнозирования временных рядов с созданием лаговых признаков (значения доходности за последние 1, 3, 6, 12 месяцев), скользящих статистик (среднее, медиана, стандартное отклонение за окно 3-6 месяцев) и календарных признаков (месяц, квартал, год). Градиентный бустинг захватывает нелинейные зависимости между лагами и текущей доходностью, которые недоступны для ARIMA и ETS.

Можно также рассмотреть нейросетевые модели для анализа временных рядов. Речь идет об архитектурах LSTM (Long Short-Term Memory) и Temporal Fusion Transformer.

LSTM хорошо справляется с улавливанием долгосрочных зависимостей в данных и обычно работает с входным окном в 50–100 предыдущих точек. Типичная конфигурация модели состоит из 2–3 слоев LSTM с 64–128 нейронами, dropout на уровне 0.2–0.3 для предотвращения переобучения.

Добавление нейросетевых моделей в ансамбль целесообразно при наличии длинной истории (10+ лет месячных данных или 3+ года дневных данных) и вычислительных ресурсов для обучения. Веса нейросетевых компонентов в ансамбле назначаются через валидацию или мета-модель стекинга. Гибридный ансамбль (статистика + бустинг + нейросети) снижает RMSE на 15-20% относительно базового статистического подхода, но увеличивает время обучения в 5-10 раз.

Обогащение признаками

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

  • Инфляция (CPI, PCE) — определяет реальную доходность облигаций и ожидания по ставкам ФРС;
  • Безработица (unemployment rate) — индикатор состояния экономики, влияет на решения ФРС по процентным ставкам;
  • Динамика ВВП (GDP growth) — отражает общий экономический рост и спрос на капитал, эффективна для долгосрочных прогнозов (12+ месяцев).
👉🏻  Продвинутые способы кросс-валидации, разделения выборок рядов: Expanding Window Splitter, Blocked Time Series Split и другие

Данные денежного рынка предоставляют высокочастотную информацию о ожиданиях участников:

  • Ставки межбанковского кредитования (SOFR, EFFR) — отражают краткосрочную стоимость капитала;
  • Спреды долгосрочных и краткосрочных ставок (10Y-2Y, 10Y-3M) — сигнализируют о ожиданиях рецессии при инверсии кривой;
  • Кредитные спреды (разница между корпоративными и казначейскими облигациями) — отражают восприятие риска и бегство в качество.

Технические индикаторы волатильности захватывают краткосрочную неопределенность:

  • Индекс MOVE — измеряет ожидаемую волатильность доходности на горизонте 30 дней;
  • Скользящие средние доходности (20, 50, 200 дней) — индикаторы смены тренда через пересечения;
  • Объемы торгов — аномально высокие значения предшествуют значительным движениям доходности.

Комплексное обогащение может включать до 15-25 экзогенных переменных. Модели машинного обучения (LightGBM, XGBoost) автоматически выбирают значимые признаки через механизм feature importance. Статистические модели (ARIMAX, Prophet) требуют ручного отбора 3-5 ключевых признаков для избежания переобучения.

Выводы

Ансамбль из AutoETS, AutoARIMA и SeasonalNaive показал себя неплохо на реальных финансовых данных. Продемонстрированный подход позволяет уменьшить ошибку прогноза на 5–11% по сравнению с каждой моделью по отдельности. Тем не менее сами метрики (RMSE = 0.61%, MAE = 0.53%, MAPE = 12.79%) говорят о том, что модель еще далека от идеала.

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

Повысить точность прогнозов можно, добавив в ансамбль модели машинного обучения, например LightGBM или XGBoost, которые умеют улавливать нелинейные зависимости. Также полезно включить дополнительные признаки: инфляцию, уровень безработицы, спреды процентных ставок и показатели волатильности рынка облигаций. Эти факторы напрямую влияют на доходность US Notes и могут снизить RMSE еще на 15–20%.

Также отмечу, что текущая реализация уже достаточно хороша в плане:

  1. Обучение ансамбля занимает минуты на обычном ноутбуке, не требовательно к GPU и больших объемам данных;
  2. Модели полностью интерпретируемы: можно точно понять, какой компонент (тренд, сезонность или авторегрессия) и в какой степени повлиял на прогноз.

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