Skip to content

Commit 1934a32

Browse files
committed
Validate CLA covariance invertibility
1 parent c524c6e commit 1934a32

2 files changed

Lines changed: 23 additions & 4 deletions

File tree

pypfopt/cla.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ def _eval_sr(self, a, w0, w1):
327327
c = np.dot(np.dot(w.T, self.cov_matrix), w)[0, 0] ** 0.5
328328
return b / c
329329

330+
@staticmethod
331+
def _invert_covariance_matrix(cov_matrix):
332+
try:
333+
return np.linalg.inv(cov_matrix)
334+
except np.linalg.LinAlgError as exc:
335+
raise ValueError(
336+
"CLA requires a positive definite covariance matrix"
337+
) from exc
338+
330339
def _solve(self):
331340
# Compute the turning points,free sets and weights
332341
f, w = self._init_algo()
@@ -339,7 +348,7 @@ def _solve(self):
339348
l_in = None
340349
if len(f) > 1:
341350
covarF, covarFB, meanF, wB = self._get_matrices(f)
342-
covarF_inv = np.linalg.inv(covarF)
351+
covarF_inv = self._invert_covariance_matrix(covarF)
343352
j = 0
344353
for i in f:
345354
lam, bi = self._compute_lambda(
@@ -354,7 +363,7 @@ def _solve(self):
354363
b = self._get_b(f)
355364
for i in b:
356365
covarF, covarFB, meanF, wB = self._get_matrices(f + [i])
357-
covarF_inv = np.linalg.inv(covarF)
366+
covarF_inv = self._invert_covariance_matrix(covarF)
358367
lam, bi = self._compute_lambda(
359368
covarF_inv,
360369
covarFB,
@@ -371,7 +380,7 @@ def _solve(self):
371380
# 3) compute minimum variance solution
372381
self.ls.append(0)
373382
covarF, covarFB, meanF, wB = self._get_matrices(f)
374-
covarF_inv = np.linalg.inv(covarF)
383+
covarF_inv = self._invert_covariance_matrix(covarF)
375384
meanF = np.zeros(meanF.shape)
376385
else:
377386
# 4) decide lambda
@@ -383,7 +392,7 @@ def _solve(self):
383392
self.ls.append(l_out)
384393
f.append(i_out)
385394
covarF, covarFB, meanF, wB = self._get_matrices(f)
386-
covarF_inv = np.linalg.inv(covarF)
395+
covarF_inv = self._invert_covariance_matrix(covarF)
387396
# 5) compute solution vector
388397
wF, g = self._compute_w(covarF_inv, covarFB, meanF, wB)
389398
for i in range(len(f)):

tests/test_cla.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ def test_cla_max_sharpe_short():
5252
assert sharpe > long_only_sharpe
5353

5454

55+
def test_cla_max_sharpe_rejects_singular_covariance():
56+
mu = np.array([0.1, 0.2])
57+
cov = np.array([[0.01, 0.01], [0.01, 0.01]])
58+
59+
cla = CLA(mu, cov)
60+
61+
with pytest.raises(ValueError, match="positive definite covariance matrix"):
62+
cla.max_sharpe()
63+
64+
5565
def test_cla_custom_bounds():
5666
bounds = [(0.01, 0.13), (0.02, 0.11)] * 10
5767
cla = setup_cla(weight_bounds=bounds)

0 commit comments

Comments
 (0)