コンテンツへスキップ
ライブラリの全資料

特性に応じた条件付きオートエンコーダによる資産価格モデル

コード Machine Learning for Trading

サマリー

この実装では、資産価格評価のための条件付きオートエンコーダを説明します。ベータネットワークは各株式の特性を潜在ファクターの負荷量に変換し、線形ファクターネットワークは運用ポートフォリオのリターンをファクターリターンに変換します。モデルは対応する負荷量とファクター値を合計して株式リターンを予測するため、企業特性に応じてエクスポージャーが変化し、ファクターはポートフォリオの運用商品から導出されます。

コードには学習済みの負荷量とファクターを取得する機能もあり、モデルの構成要素を調べる際に役立ちます。L1ペナルティが適用されるのはベータネットワークの隠れ層の重みだけです。バイアスと最終の負荷量層は対象外で、隠れ層がない場合はゼロを返します。これはアーキテクチャの解説であり、実証研究ではありません。学習手順、データセット、予測結果、他の資産価格モデルとの比較は示していません。

主なアイデア

  • ベータネットワークは、株式の特性を銘柄ごとの潜在ファクター負荷量に変換します。
  • 線形ネットワークは、運用ポートフォリオの入力をファクターリターンに変換します。
  • 負荷量とファクターの積を合計して予測リターンを作ります。
  • 正則化はベータネットワークの隠れ層の重みにスパース性を促しますが、出力層の重みはペナルティの対象にしません。
  • この実装はモデル構造を定義していますが、実証評価は行っていません。

タグ

全文
# cae.py


```py
"""Conditional Autoencoder for Asset Pricing (GKX 2021).

Architecture:


- ConditionalAutoencoder: predicted return = dot(betas, factors)

Reference: Gu, Kelly, Xiu (2021) "Autoencoder Asset Pricing Models"
"""

from __future__ import annotations

import torch
import torch.nn as nn


class BetaNetwork(nn.Module):
    """Maps stock characteristics to per-stock factor loadings."""

    def __init__(self, n_characteristics: int, n_factors: int, hidden_units: tuple = (32,)):
        super().__init__()

        layers: list[nn.Module] = []
        in_features = n_characteristics

        for units in hidden_units:
            layers.append(nn.Linear(in_features, units))
            layers.append(nn.ReLU())
            in_features = units

        layers.append(nn.Linear(in_features, n_factors))
        self.network = nn.Sequential(*layers)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.network(x)


class FactorNetwork(nn.Module):
    """Maps managed portfolio returns to factor returns (linear, no activation)."""

    def __init__(self, n_instruments: int, n_factors: int):
        super().__init__()
        self.linear = nn.Linear(n_instruments, n_factors, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.linear(x)


class ConditionalAutoencoder(nn.Module):
    """Conditional Autoencoder for Asset Pricing (GKX Architecture).

    Architecture:
    - Beta Network: characteristics -> ReLU -> factor loadings (with L1 regularization)
    - Factor Network: managed portfolios -> factor returns (linear)
    - Output: dot product of betas and factors

    Args:
        n_characteristics: Number of stock characteristics (L)
        n_instruments: Number of managed portfolio instruments (L+1 typically)
        n_factors: Number of latent factors (K)
        hidden_units: Tuple of hidden layer sizes for BetaNetwork
    """

    def __init__(
        self,
        n_characteristics: int,
        n_instruments: int,
        n_factors: int = 6,
        hidden_units: tuple = (32,),
    ):
        super().__init__()

        self.n_factors = n_factors
        self.n_characteristics = n_characteristics
        self.n_instruments = n_instruments

        self.beta_net = BetaNetwork(n_characteristics, n_factors, hidden_units)
        self.factor_net = FactorNetwork(n_instruments, n_factors)

    def forward(self, characteristics: torch.Tensor, portfolios: torch.Tensor) -> torch.Tensor:
        """Forward pass.

        Args:
            characteristics: (batch_size, n_characteristics) per-stock
            portfolios: (batch_size, n_instruments) managed portfolio features

        Returns:
            predicted_returns: (batch_size,)
        """
        betas = self.beta_net(characteristics)  # (batch, n_factors)
        factors = self.factor_net(portfolios)  # (batch, n_factors)
        pred = (betas * factors).sum(dim=1)
        return pred

    def get_betas(self, characteristics: torch.Tensor) -> torch.Tensor:
        """Extract factor loadings."""
        return self.beta_net(characteristics)

    def get_factors(self, portfolios: torch.Tensor) -> torch.Tensor:
        """Extract factor returns."""
        return self.factor_net(portfolios)


def l1_regularization(model: ConditionalAutoencoder, lambda_l1: float) -> torch.Tensor:
    """L1 regularization on BetaNetwork hidden Dense weights only.

    Matches the GKX (2021) reference, which applies ``kernel_regularizer='L1'``
    only to the *hidden* Dense layers. We exclude biases (sparsity is not
    meaningful) and the final output ``Linear(in, n_factors)`` layer (factor
    loadings should not be pushed to zero by an external regularizer — the
    autoencoder itself learns their magnitudes).
    """
    layers = [m for m in model.beta_net.network if isinstance(m, nn.Linear)]
    if len(layers) <= 1:
        # No hidden layers (CA0 — direct linear projection); no L1 to apply.
        return torch.zeros((), device=next(model.parameters()).device)
    hidden_layers = layers[:-1]  # exclude the final output Linear
    l1 = sum(layer.weight.abs().sum() for layer in hidden_layers)
    return lambda_l1 * l1

```

出典を明記したうえで、ライセンスに従って全文を掲載しています。 ライセンス: MIT

この要約は原文をもとにStratmillのリサーチエージェントが作成したもので、出典の複製ではありません。