def test_shrinkage_coef(self): """Dynamic vs at construction.""" n_samples = 3 n_channels = 4 n_assets = 5 x = torch.rand((n_samples, n_channels, n_assets)) * 100 layer_1 = CovarianceMatrix(sqrt=False, shrinkage_strategy='diagonal', shrinkage_coef=0.3) layer_2 = CovarianceMatrix(sqrt=False, shrinkage_strategy='diagonal', shrinkage_coef=None) assert torch.allclose( layer_1(x), layer_2(x, 0.3 * torch.ones(n_samples, dtype=x.dtype))) x_ = torch.rand((n_channels, n_assets)) * 100 x_stacked = torch.stack([x_, x_, x_], dim=0) res_1 = layer_1(x_stacked) res_2 = layer_2(x_stacked, torch.tensor([0.2, 0.3, 0.6])) assert torch.allclose(res_1[0], res_1[1]) assert torch.allclose(res_1[1], res_1[2]) assert torch.allclose(res_1[2], res_1[0]) assert not torch.allclose(res_2[0], res_2[1]) assert not torch.allclose(res_2[1], res_2[2]) assert not torch.allclose(res_2[2], res_2[0])
def test_n_parameters(self, sqrt): layer = CovarianceMatrix() n_parameters = sum(p.numel() for p in layer.parameters() if p.requires_grad) assert n_parameters == 0
def test_wrong_construction(self): with pytest.raises(ValueError): CovarianceMatrix(shrinkage_strategy='fake') with pytest.raises(ValueError): layer = CovarianceMatrix(shrinkage_coef=None) layer(torch.ones(1, 5, 2))
def test_basic(self, Xy_dummy, sqrt, shrinkage_strategy): X, _, _, _ = Xy_dummy n_samples, n_channels, lookback, n_assets = X.shape X_ = X.mean(dim=1) if n_channels == 1: with pytest.raises(ZeroDivisionError): CovarianceMatrix(sqrt, shrinkage_strategy=shrinkage_strategy)(X_) else: out = CovarianceMatrix(sqrt, shrinkage_strategy=shrinkage_strategy)(X_) assert out.shape == (n_samples, n_assets, n_assets)
def test_sqrt_works(self): n_samples = 3 n_channels = 4 n_assets = 5 x = torch.rand((n_samples, n_channels, n_assets)) * 100 cov = CovarianceMatrix(sqrt=False)(x) cov_sqrt = CovarianceMatrix(sqrt=True)(x) assert (n_samples, n_assets, n_assets) == cov.shape == cov_sqrt.shape for i in range(n_samples): assert torch.allclose(cov[i], cov_sqrt[i] @ cov_sqrt[i], atol=1e-2)
def __init__( self, n_external, n_assets, n_channels=5, hidden_size=32, max_weight=0.15, force_symmetric=True, p=0.2, ): super().__init__() self.force_symmetric = force_symmetric self.matrix = torch.nn.Parameter(torch.eye(n_assets), requires_grad=True) self.exp_returns = torch.nn.Parameter(torch.zeros(n_assets), requires_grad=True) self.norm_layer = torch.nn.InstanceNorm2d(n_channels, affine=True) self.collapse_external = AverageCollapse() self.transform_layer = RNN(n_channels, hidden_size=hidden_size) self.dropout_layer = torch.nn.Dropout(p=p) self.dropout_layer2 = torch.nn.Dropout(p=p) self.time_collapse_layer = AttentionCollapse(n_channels=hidden_size) self.conv1 = Conv(n_input_channels=hidden_size, n_output_channels=1, method="1D") # self.conv2 = Conv(n_input_channels=3, n_output_channels=1, method="1D") self.linear_transform = torch.nn.Linear(n_external, n_assets) self.linear_2 = torch.nn.Linear(n_external, n_assets) self.covariance_layer = CovarianceMatrix(sqrt=False, shrinkage_strategy="diagonal") self.gamma_sqrt = torch.nn.Parameter(torch.ones(1), requires_grad=True) self.alpha = torch.nn.Parameter(torch.ones(1), requires_grad=True) self.preliminar_weights_layer = NumericalMarkowitz( n_assets, max_weight=max_weight)
def stupid_compute(w, y): """Straightforward implementation with list comprehensions.""" from deepdow.layers import CovarianceMatrix n_samples, n_assets = w.shape covar = CovarianceMatrix(sqrt=False)(y[:, 0, ...]) # returns_channel=0 var = torch.cat([(w[[i]] @ covar[i]) @ w[[i]].permute(1, 0) for i in range(n_samples)], dim=0) vol = torch.sqrt(var) lhs = vol / n_assets rhs = torch.cat([(1 / vol[i]) * w[[i]] * (w[[i]] @ covar[i]) for i in range(n_samples)], dim=0) res = torch.tensor([((lhs[i] - rhs[i])**2).sum() for i in range(n_samples)]) return res