Source code for fynance.models.loss._base

#!/usr/bin/env python3
# coding: utf-8

""" Base class for financial loss functions.

Defines :class:`BaseLoss`, the common foundation for all differentiable
loss functions in :mod:`fynance.models.loss`.

"""

from __future__ import annotations

# Third-party packages
import torch
import torch.nn

__all__ = ['BaseLoss']

#: Hard magnitude bound for ratio-style losses (Sharpe / Sortino / Calmar /
#: Omega). It is far above any plausible real ratio (which are O(1)-O(10)), so
#: it never touches a normal-case value; it only caps the runaway that a
#: collapsing risk denominator would otherwise produce, keeping the loss finite
#: and its gradients well-scaled.
MAX_RATIO: float = 1e3


[docs] class BaseLoss(torch.nn.Module): """ Base class for differentiable financial loss functions. Holds the shared hyper-parameters (risk-free rate, annualization period, numerical stabilizer) and enforces that inputs are :class:`torch.Tensor`. Subclass and implement :meth:`forward` to define a new loss. Parameters ---------- rf : float, optional Annualized risk-free rate. Default is 0. period : int, optional Number of periods per year used for annualization. Default is 252. eps : float, optional Small constant added to denominators to avoid division by zero. Default is 1e-8. """ def __init__(self, rf: float = 0., period: int = 252, eps: float = 1e-8): super().__init__() self.rf = rf self.period = period self.eps = eps @property def _rf_per_period(self) -> float: """ Per-period risk-free rate, recomputed from the live ``rf``/``period``. Computed as a property (not cached in ``__init__``) so that mutating ``loss.rf`` or ``loss.period`` after construction takes effect on the next :meth:`forward` instead of being a silent no-op. """ return self.rf / self.period def _check_tensor(self, x: object) -> None: """ Raise TypeError if *x* is not a :class:`torch.Tensor`. """ if not isinstance(x, torch.Tensor): raise TypeError( f"Expected torch.Tensor, got {type(x).__name__}. " "Convert numpy arrays with torch.from_numpy() before passing " "to this loss." )