Source code for macrosynergy.learning.forecasting.torch.losses.sharpe_loss

import torch 
import torch.nn as nn

[docs]class MultiOutputSharpe(nn.Module): """ Negative Sharpe ratio loss for multi-output regression problems. Notes ----- When a neural network is designed so that the output can be interpreted as signals or portfolio weights for each output, a stylized Sharpe ratio can be calculated by multiplying the true returns by the respective signals or weights, before downsampling to portfolio returns. The Sharpe ratio, excluding trading frictions such as transaction costs, can be calculated over the batch. Neural networks are most naturally formulated as minimization problems, so the negative Sharpe ratio is used as a loss function. """ def __init__(self, skip_validation = True, unbiased = True): super().__init__() # Checks if not isinstance(skip_validation, bool): raise TypeError("skip_validation must be a boolean") if not isinstance(unbiased, bool): raise TypeError("unbiased must be a boolean") # Attributes self.skip_validation = skip_validation self.unbiased = unbiased
[docs] def forward(self, y_pred, y_true): """ Evaluate batch negative Sharpe ratio loss. Parameters ---------- y_pred : torch.Tensor Predicted outputs (signals or portfolio weights). y_true : torch.Tensor True outputs (returns). """ if not self.skip_validation: self._check_forward_params(y_pred, y_true) returns = y_pred * y_true portfolio_returns = torch.sum(returns, axis = 1) return - torch.mean(portfolio_returns)/torch.std(portfolio_returns, unbiased = self.unbiased)
def _check_forward_params(self, y_pred, y_true): """ Check parameters for forward method """ if not isinstance(y_true, torch.Tensor): raise TypeError("y_true must be a torch.Tensor") if y_true.shape[1] < 2: raise ValueError("y_true must have at least 2 outputs (shape[1] >= 2)") if not isinstance(y_pred, torch.Tensor): raise TypeError("y_pred must be a torch.Tensor") if y_pred.shape[1] < 2: raise ValueError("y_pred must have at least 2 outputs (shape[1] >= 2)") if y_true.shape != y_pred.shape: raise ValueError("y_true and y_pred must have the same shape")