עבור לתוכן
כל מסמכי הספרייה

הפסד Sharpe חסין עם עלויות תחלופה ללמידת תיק

קוד Machine Learning for Trading

סיכום

מודול זה מגדיר פונקציית מטרה גזירה ללמידת תיק מקצה לקצה. הוא ממיר משקולות סיכון מוגבלות ברמת הנכס לחשיפות תיק באמצעות התאמה לתנודתיות, מחשב תשואות גולמיות ממוצעות על פני נכסים זמינים, ואפשר גם מפחית עלויות עסקה על סמך שינויים מהמשקולות הקודמות. אפשר להחריג תקופת חימום מהערכת סדרת התשואות.

פונקציית המטרה משלבת יחס Sharpe מאוחד עם מינימום רך של יחסי Sharpe לכל חלון, במשקל פרמטר שניתן לכוונון, והאימון ממזער את השלילה של הציון המשולב. מבנה זה מתגמל ביצועים כוללים מותאמי סיכון ובה בעת מתחשב בחלונות חלשים יותר. הפונקציות מאפשרות להגדיר בחישוב שנתי, ייצוב מספרי, התאמת עלויות וטמפרטורת מינימום רך. המסמך מספק הגדרות מימוש, אך אינו כולל תוצאות אמפיריות, הנחיות כיול או ראיות לכך שפונקציית המטרה משפרת ביצועים ממומשים; לכן התוצאות תלויות בבחירת משקולות, מסכות, עלויות והיפר-פרמטרים.

רעיונות מרכזיים

  • תשואות התיק מחושבות ממשקולות סיכון ומהתאמה לתנודתיות, וממוצעות על פני הנכסים הזמינים בכל צעד זמן.
  • אפשר למדל עלויות עסקה כענישה על שינויים בחשיפת התיק לעומת הצעד הקודם.
  • ההפסד משלב Sharpe מאוחד עם מינימום רך של יחסי Sharpe ברמת החלון.
  • אפשר להחריג תצפיות מתקופת החימום מחישובי Sharpe.
  • המודול מגדיר פונקציית מטרה, אך אינו מספק ראיות אמפיריות או ערכי פרמטרים מומלצים.

תגיות

הטקסט המלא
# losses.py


```py
"""Loss functions for DeePM-style end-to-end portfolio learning."""

from __future__ import annotations

from dataclasses import dataclass

import torch


@dataclass(frozen=True, slots=True)
class LossOutput:
    """Outputs returned by the robust Sharpe loss."""

    loss: torch.Tensor
    sharpe_pool: torch.Tensor
    softmin_sharpe: torch.Tensor
    objective: torch.Tensor
    net_returns: torch.Tensor


def compute_net_portfolio_returns(
    *,
    p: torch.Tensor,
    y_fwd1: torch.Tensor,
    vol_scale: torch.Tensor,
    mask: torch.Tensor,
    costs: torch.Tensor | None,
    gamma_cost: float,
) -> torch.Tensor:
    """Compute net portfolio returns (Eq. 13).

    Parameters
    ----------
    p: Risk weights in (-1, 1), shape (B, T, N).
    y_fwd1: Vol-scaled forward returns, shape (B, T, N).
    vol_scale: Volatility scaling, shape (B, T, N).
    mask: Availability mask, shape (B, T, N).
    costs: Per-asset cost coefficients, shape (N,) or (N, 1). None to skip.
    gamma_cost: Global cost scaling factor.

    Returns
    -------
    Net portfolio return series, shape (B, T).
    """
    b, t, n = p.shape

    w = vol_scale * p
    w_prev = torch.cat(
        [torch.zeros((b, 1, n), device=w.device, dtype=w.dtype), w[:, :-1, :]], dim=1
    )

    gross = (mask * p * y_fwd1).sum(dim=-1)
    n_t = mask.sum(dim=-1).clamp(min=1.0)
    gross = gross / n_t

    if costs is None:
        return gross

    if (costs.ndim == 2 and costs.shape[1] == 1) or (costs.ndim == 1 and costs.shape[0] == n):
        c = costs.view(1, 1, n)
    else:
        raise ValueError("costs must have shape (N,) or (N,1)")

    turnover = torch.abs(w - w_prev)
    cost = (mask * c * turnover).sum(dim=-1)
    cost = (gamma_cost * cost) / n_t

    return gross - cost


def sharpe_ratio(
    returns: torch.Tensor,
    *,
    annualization_factor: float,
    eps: float,
    dim: int | None = None,
) -> torch.Tensor:
    """Compute a differentiable Sharpe ratio."""
    if dim is None:
        mu = returns.mean()
        var = returns.var(unbiased=False)
    else:
        mu = returns.mean(dim=dim)
        var = returns.var(dim=dim, unbiased=False)

    return (annualization_factor**0.5) * mu / torch.sqrt(var + eps)


def softmin_sharpe(
    window_sharpes: torch.Tensor,
    *,
    tau: float,
) -> torch.Tensor:
    """Soft minimum of window-wise Sharpe ratios (Eq. 33)."""
    return -tau * torch.log(torch.mean(torch.exp(-window_sharpes / tau)))


def robust_sharpe_loss(
    *,
    p: torch.Tensor,
    y_fwd1: torch.Tensor,
    vol_scale: torch.Tensor,
    mask: torch.Tensor,
    costs: torch.Tensor | None,
    burn_in: int,
    gamma_cost: float,
    annualization_factor: float,
    eps: float,
    tau: float,
    lambda_soft: float,
) -> LossOutput:
    """Compute DeePM robust objective loss (Eq. 31).

    L(theta) = - SR_pool(R) - lambda * SoftMin_tau({SR_b}).
    """
    net_r = compute_net_portfolio_returns(
        p=p,
        y_fwd1=y_fwd1,
        vol_scale=vol_scale,
        mask=mask,
        costs=costs,
        gamma_cost=gamma_cost,
    )

    net_r_eff = net_r[:, burn_in:] if burn_in > 0 else net_r

    sr_pool = sharpe_ratio(
        net_r_eff.reshape(-1), annualization_factor=annualization_factor, eps=eps
    )
    sr_windows = sharpe_ratio(net_r_eff, annualization_factor=annualization_factor, eps=eps, dim=1)

    sr_softmin = softmin_sharpe(sr_windows, tau=tau)

    objective = sr_pool + lambda_soft * sr_softmin
    loss = -objective

    return LossOutput(
        loss=loss,
        sharpe_pool=sr_pool.detach(),
        softmin_sharpe=sr_softmin.detach(),
        objective=objective.detach(),
        net_returns=net_r.detach(),
    )

```

מוצג במלואו בציון המקור ובהתאם לרישיון שלו. רישיון: MIT

הסיכום נכתב בידי סוכן המחקר של Stratmill על סמך המקור; הוא אינו העתק של המקור.