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

import torch 
import torch.nn as nn 

[docs]class MultiOutputMCR(nn.Module): """ Negative mean-concentration risk ratio loss for multi-output regression problems. Notes ----- By mean-concentration risk ratio, we refer to the ratio of the mean return within a time period, to the standard deviation of returns within that time period. This differs from a Sharpe ratio in that the Sharpe is a temporal quantity, whereas this statistic is cross-sectional. Maximisation of such a statistic would encourage positive returns at each time period whilst penalising diversity in the cross-sectional return distribution. The goal is to encourage prevent the model from concentrating returns in a small subset of the outputs. This statistic can be calculated for each sample in a batch, and then averaged over the batch. Neural networks are most naturally formulated as minimization problems, so the negative mean-concentration risk 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 mean-concentration risk 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_true * y_pred mean_returns = torch.mean(returns, axis = 1) std_returns = torch.std(returns, axis = 1) return - torch.mean(mean_returns / std_returns)
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")