def test_basic(self, Xy_dummy, formulation): eps = 1e-5 X, _, _, _ = Xy_dummy dtype, device = X.dtype, X.device n_samples, n_channels, lookback, n_assets = X.shape rets = X.mean(dim=(1, 2)) with pytest.raises(ValueError): SoftmaxAllocator(temperature=None, formulation=formulation, n_assets=n_assets)(rets, temperature=None) weights = SoftmaxAllocator(temperature=2, formulation=formulation, n_assets=n_assets)(rets) assert torch.allclose( weights, SoftmaxAllocator( temperature=None, formulation=formulation, n_assets=n_assets)( rets, 2 * torch.ones(n_samples, dtype=dtype, device=device))) assert weights.shape == (n_samples, n_assets) assert weights.dtype == X.dtype assert weights.device == X.device assert torch.all(-eps <= weights) and torch.all(weights <= 1 + eps) assert torch.allclose(weights.sum(dim=1), torch.ones(n_samples).to(dtype=dtype, device=device), atol=eps)
def test_errors(self): with pytest.raises(ValueError): SoftmaxAllocator(formulation='wrong') with pytest.raises(ValueError): SoftmaxAllocator(formulation='variational', n_assets=None) with pytest.raises(ValueError): SoftmaxAllocator(formulation='analytical', max_weight=0.3) with pytest.raises(ValueError): SoftmaxAllocator(formulation='variational', max_weight=0.09, n_assets=10)
def test_contstrained(self, max_weight): rets = torch.tensor([[1.7909, -2, -0.6818, -0.4972, 0.0333]]) w_const = SoftmaxAllocator(n_assets=5, temperature=1, max_weight=max_weight, formulation='variational')(rets) w_unconst = SoftmaxAllocator(n_assets=5, temperature=1, max_weight=1, formulation='variational')(rets) assert not torch.allclose(w_const, w_unconst) assert w_const.max().item() == pytest.approx(max_weight, abs=1e-4)
def test_uniform(self, formulation): rets = torch.ones(2, 5) weights = SoftmaxAllocator(formulation=formulation, n_assets=5, temperature=1)(rets) assert torch.allclose(weights, rets / 5, atol=1e-4)
def __init__(self, n_assets, lookback, p=0.5): super().__init__() n_features = n_assets * lookback self.dropout_layer = torch.nn.Dropout(p=p) self.dense_layer = torch.nn.Linear(n_features, n_assets, bias=True) self.allocate_layer = SoftmaxAllocator(temperature=None) self.temperature = torch.nn.Parameter(torch.ones(1), requires_grad=True)
def test_equality_formulations(self, dtype_device): dtype, device = dtype_device n_samples, n_assets = 3, 6 layer_analytical = SoftmaxAllocator(formulation='analytical') layer_variational = SoftmaxAllocator(formulation='variational', n_assets=n_assets) torch.manual_seed(2) rets = torch.randint(10, size=(n_samples, n_assets)) + torch.rand(size=(n_samples, n_assets)) rets = rets.to(device=device, dtype=dtype) weights_analytical = layer_analytical(rets) weights_variational = layer_variational(rets) assert weights_analytical.shape == weights_variational.shape assert weights_analytical.dtype == weights_variational.dtype assert weights_analytical.device == weights_variational.device assert torch.allclose(weights_analytical, weights_variational, atol=1e-4)
def __init__( self, n_assets, max_weight=0.2, force_symmetric=True, ): 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.temperature = torch.nn.Parameter(torch.ones(1), requires_grad=True) self.gamma_sqrt = torch.nn.Parameter(torch.ones(1), requires_grad=True) self.alpha = torch.nn.Parameter(torch.ones(1), requires_grad=True) self.pre_alloc_layer = NumericalMarkowitz(n_assets, max_weight) self.allocation_layer = SoftmaxAllocator( n_assets=n_assets, temperature=None, max_weight=max_weight, formulation="variational", )
fig, axs = plt.subplots(len(temperatures), len(max_weights), sharex=True, sharey=True, figsize=(15, 5)) cbar_ax = fig.add_axes([.91, .3, .03, .4]) for r, temperature in enumerate(temperatures): for c, max_weight in enumerate(max_weights): sparsemax = SparsemaxAllocator(n_assets, max_weight=max_weight, temperature=temperature) softmax = SoftmaxAllocator(n_assets=n_assets, temperature=temperature, max_weight=max_weight, formulation='variational') w_sparsemax = sparsemax(logits).detach().numpy() w_softmax = softmax(logits).detach().numpy() df = pd.DataFrame(np.concatenate([w_softmax, w_sparsemax], axis=0), index=['softmax', 'sparsemax']) axs[r, c].set_title('temp={}, max_weight={}'.format( temperature, max_weight)) sns.heatmap(df, vmin=0, vmax=1, center=0.5, cmap='hot',