SortinoLoss

Defined in fynance.models.loss

class SortinoLoss(rf=0., period=252, eps=1e-8)[source]

Bases: BaseLoss

Negative Sortino ratio as a differentiable loss.

Minimizing this loss penalizes downside returns only, unlike SharpeLoss which penalizes both tails symmetrically.

Parameters:
rffloat, optional

Annualized risk-free rate. Default is 0.

periodint, optional

Number of periods per year. Default is 252.

epsfloat, optional

Bare numerical stabilizer added to the returns-scaled downside floor as a backstop for the degenerate all-zero batch (see Notes). Default is 1e-8.

See also

SharpeLoss, DirectionalAccuracyLoss

Notes

The loss is defined as:

\[\mathcal{L} = -\frac{\mu(r - rf_p)} {\sqrt{\mu(\text{ReLU}(-(r - rf_p))^2) + \varepsilon}}\]

where \(r\) is y_pred and \(rf_p = rf / period\). The denominator is a differentiable proxy for the downside deviation; its magnitude differs from the numpy sortino evaluation metric.

This is a training proxy — the value is not comparable to the numpy sortino evaluation metric.

The downside deviation is \(O(\text{returns})\), so a fixed absolute eps inside the square root is dimensionally wrong: on an all-gains batch (zero downside) the loss would explode (e.g. -100). The downside is therefore floored with a returns-scaled value |excess|.mean() / MAX_RATIO (plus a bare eps backstop for the degenerate all-zero batch). This caps the ratio at roughly MAX_RATIO in the low-downside regime regardless of the return scale, keeping the loss finite and bounded while preserving the sign convention.

The ratio is then passed through a smooth saturating map, MAX_RATIO * tanh(ratio / MAX_RATIO), instead of a hard clamp. A hard clamp pins the loss to a constant on a low-downside batch and so kills the gradient in exactly the strong-uptrend regime training still wants to push on; tanh is near-linear for normal-regime ratios (leaving their numerics unchanged) yet keeps a residual, non-zero gradient when the ratio is large.

Examples

>>> import torch
>>> from fynance.models.loss import SortinoLoss
>>> returns = torch.tensor([-0.01, 0.02, 0.01, -0.005, 0.03])
>>> loss_fn = SortinoLoss()
>>> loss = loss_fn(returns)
>>> loss.shape
torch.Size([])
forward(y_pred, y_true=None)[source]

Compute the negative Sortino ratio.

Parameters:
y_predtorch.Tensor

Predicted return series, shape (T,) or (T, M).

y_truetorch.Tensor, optional

Not used; accepted for API compatibility with PyTorch criterions.

Returns:
torch.Tensor

Scalar loss value (negative Sortino ratio proxy).

Raises:
TypeError

If y_pred is not a torch.Tensor.