Skip to content

Commit

Permalink
fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
tschm committed Jan 1, 2025
1 parent 8bf1ecd commit f0eebea
Show file tree
Hide file tree
Showing 16 changed files with 52 additions and 68 deletions.
19 changes: 19 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ repos:
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-yaml

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.8.4'
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
# Run the formatter
- id: ruff-format

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.43.0
Expand Down Expand Up @@ -38,3 +41,19 @@ repos:
files: ^(cvx)
args:
['--license-filepath', 'copyright.txt', '--no-extra-eol']

- repo: https://github.com/rhysd/actionlint
rev: v1.7.5
hooks:
- id: actionlint
args: [-ignore, SC]

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.23
hooks:
- id: validate-pyproject

- repo: https://github.com/crate-ci/typos
rev: v1.29.0
hooks:
- id: typos
4 changes: 1 addition & 3 deletions book/marimo/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ def __(__file__):
@app.cell
def __(path, pd):
# Load some historic stock prices
prices = pd.read_csv(
path / "data" / "stock_prices.csv", index_col=0, parse_dates=True, header=0
)
prices = pd.read_csv(path / "data" / "stock_prices.csv", index_col=0, parse_dates=True, header=0)

# Estimate a series of historic covariance matrices
returns = prices.pct_change().dropna(axis=0, how="all")
Expand Down
4 changes: 1 addition & 3 deletions book/marimo/factormodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ def __():
@app.cell
def __(path, pd):
# Load some historic stock prices
prices = pd.read_csv(
path / "data" / "stock_prices.csv", index_col=0, parse_dates=True, header=0
)
prices = pd.read_csv(path / "data" / "stock_prices.csv", index_col=0, parse_dates=True, header=0)

# Estimate a series of historic covariance matrices
returns = prices.pct_change().dropna(axis=0, how="all")
Expand Down
10 changes: 2 additions & 8 deletions book/marimo/large.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,7 @@ def __(beta, cvx, factors, minrisk_problem, np, pd, ret, triangle):
triangle.update(
exposure=beta.values,
cov=factors.cov().values,
idiosyncratic_risk=pd.DataFrame(
data=ret - factors @ beta, index=ret.index, columns=ret.columns
)
.std()
.values,
idiosyncratic_risk=pd.DataFrame(data=ret - factors @ beta, index=ret.index, columns=ret.columns).std().values,
lower_assets=np.zeros(1000),
upper_assets=np.ones(1000),
lower_factors=-0.1 * np.ones(100),
Expand All @@ -86,9 +82,7 @@ def __(beta, factors, minrisk_problem, np, pd, ret, triangle, w, y):
triangle.update(
exposure=beta.values,
cov=factors.cov().values,
idiosyncratic_risk=pd.DataFrame(
data=ret - factors @ beta, index=ret.index, columns=ret.columns
)
idiosyncratic_risk=pd.DataFrame(data=ret - factors @ beta, index=ret.index, columns=ret.columns)
.std()
.values,
lower_assets=np.zeros(1000),
Expand Down
3 changes: 1 addition & 2 deletions cvx/portfolio/min_risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
def minrisk_problem(riskmodel, weights, **kwargs):
problem = cp.Problem(
cp.Minimize(riskmodel.estimate(weights, **kwargs)),
[cp.sum(weights) == 1.0, weights >= 0]
+ riskmodel.constraints(weights, **kwargs),
[cp.sum(weights) == 1.0, weights >= 0] + riskmodel.constraints(weights, **kwargs),
)

return problem
1 change: 1 addition & 0 deletions cvx/risk/bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Bounds"""

from __future__ import annotations

from dataclasses import dataclass
Expand Down
4 changes: 1 addition & 3 deletions cvx/risk/cvar/cvar.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ class CVar(Model):

def __post_init__(self):
self.k = int(self.n * (1 - self.alpha))
self.parameter["R"] = cvx.Parameter(
shape=(self.n, self.m), name="returns", value=np.zeros((self.n, self.m))
)
self.parameter["R"] = cvx.Parameter(shape=(self.n, self.m), name="returns", value=np.zeros((self.n, self.m)))
self.bounds = Bounds(m=self.m, name="assets")

def estimate(self, weights, **kwargs):
Expand Down
16 changes: 5 additions & 11 deletions cvx/risk/factor/factor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Factor risk model
"""
"""Factor risk model"""

from __future__ import annotations

from dataclasses import dataclass
Expand Down Expand Up @@ -59,15 +59,11 @@ def estimate(self, weights, **kwargs):
"""
Compute the total variance
"""
var_residual = cvx.norm2(
cvx.multiply(self.parameter["idiosyncratic_risk"], weights)
)
var_residual = cvx.norm2(cvx.multiply(self.parameter["idiosyncratic_risk"], weights))

y = kwargs.get("y", self.parameter["exposure"] @ weights)

return cvx.norm2(
cvx.vstack([cvx.norm2(self.parameter["chol"] @ y), var_residual])
)
return cvx.norm2(cvx.vstack([cvx.norm2(self.parameter["chol"] @ y), var_residual]))

def update(self, **kwargs):
self.parameter["exposure"].value = np.zeros((self.k, self.assets))
Expand All @@ -83,9 +79,7 @@ def update(self, **kwargs):
assert assets <= self.assets

self.parameter["exposure"].value[:k, :assets] = kwargs["exposure"]
self.parameter["idiosyncratic_risk"].value[:assets] = kwargs[
"idiosyncratic_risk"
]
self.parameter["idiosyncratic_risk"].value[:assets] = kwargs["idiosyncratic_risk"]
self.parameter["chol"].value[:k, :k] = cholesky(kwargs["cov"])
self.bounds_assets.update(**kwargs)
self.bounds_factors.update(**kwargs)
Expand Down
8 changes: 3 additions & 5 deletions cvx/risk/linalg/pca.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""PCA analysis
"""
"""PCA analysis"""

from __future__ import annotations

from collections import namedtuple
Expand Down Expand Up @@ -48,9 +48,7 @@ def pca(returns, n_components=10):
factors=factors,
exposure=pd.DataFrame(data=exposure, columns=returns.columns),
cov=factors.cov(),
systematic=pd.DataFrame(
data=factors.values @ exposure, index=returns.index, columns=returns.columns
),
systematic=pd.DataFrame(data=factors.values @ exposure, index=returns.index, columns=returns.columns),
idiosyncratic=pd.DataFrame(
data=returns.values - factors.values @ exposure,
index=returns.index,
Expand Down
4 changes: 2 additions & 2 deletions cvx/risk/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Abstract risk model
"""
"""Abstract risk model"""

from __future__ import annotations

from abc import ABC, abstractmethod
Expand Down
4 changes: 2 additions & 2 deletions cvx/risk/sample/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Risk models based on the sample covariance matrix
"""
"""Risk models based on the sample covariance matrix"""

from __future__ import annotations

from dataclasses import dataclass
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ dev = [
]

[tool.ruff]
select = ["E", "F", "I"]
line-length = 120
target-version = "py310"
exclude = [
"*__init__.py"
]

[tool.ruff.lint]
select = ["E", "F", "I"]


[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Expand Down
14 changes: 4 additions & 10 deletions tests/test_risk/test_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@ def test_raise_not_implemented():
def test_constraints():
weights = cp.Variable(3)
bounds = Bounds(m=3, name="assets")
bounds.update(
lower_assets=np.array([0.1, 0.2]), upper_assets=np.array([0.3, 0.4, 0.5])
)

assert bounds.parameter["lower_assets"].value == pytest.approx(
np.array([0.1, 0.2, 0])
)
assert bounds.parameter["upper_assets"].value == pytest.approx(
np.array([0.3, 0.4, 0.5])
)
bounds.update(lower_assets=np.array([0.1, 0.2]), upper_assets=np.array([0.3, 0.4, 0.5]))

assert bounds.parameter["lower_assets"].value == pytest.approx(np.array([0.1, 0.2, 0]))
assert bounds.parameter["upper_assets"].value == pytest.approx(np.array([0.3, 0.4, 0.5]))

assert len(bounds.constraints(weights)) == 2
12 changes: 3 additions & 9 deletions tests/test_risk/test_factor/test_factor.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@

@pytest.fixture()
def returns(resource_dir):
prices = pd.read_csv(
resource_dir / "stock_prices.csv", index_col=0, header=0, parse_dates=True
)
prices = pd.read_csv(resource_dir / "stock_prices.csv", index_col=0, header=0, parse_dates=True)
return prices.pct_change().fillna(0.0)


Expand Down Expand Up @@ -93,9 +91,7 @@ def test_estimate_risk():
assert np.array(weights.value[20:]) == pytest.approx(np.zeros(5), abs=1e-6)

# test that the exposure is correct, e.g. the factor weights match the exposure * asset weights
assert model.parameter["exposure"].value @ weights.value == pytest.approx(
y.value, abs=1e-6
)
assert model.parameter["exposure"].value @ weights.value == pytest.approx(y.value, abs=1e-6)

# test all entries of y are smaller than 0.1
assert np.all([y.value <= 0.1 + 1e-6])
Expand Down Expand Up @@ -125,8 +121,6 @@ def test_dynamic_exposure():
upper_factors=np.array([1.0]),
)

np.testing.assert_array_equal(
model.parameter["exposure"].value, np.array([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]])
)
np.testing.assert_array_equal(model.parameter["exposure"].value, np.array([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]]))

# assert False
4 changes: 1 addition & 3 deletions tests/test_risk/test_linalg/test_pca.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

@pytest.fixture()
def returns(resource_dir):
prices = pd.read_csv(
resource_dir / "stock_prices.csv", index_col=0, header=0, parse_dates=True
)
prices = pd.read_csv(resource_dir / "stock_prices.csv", index_col=0, header=0, parse_dates=True)
return prices.pct_change().fillna(0.0)


Expand Down
8 changes: 2 additions & 6 deletions tests/test_risk/test_sample/test_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ def test_min_variance():
upper_assets=np.ones(2),
)
problem.solve()
np.testing.assert_almost_equal(
weights.value, np.array([0.75, 0.25, 0.0, 0.0]), decimal=5
)
np.testing.assert_almost_equal(weights.value, np.array([0.75, 0.25, 0.0, 0.0]), decimal=5)

# It's enough to only update the value for the cholesky decomposition
riskmodel.update(
Expand All @@ -52,6 +50,4 @@ def test_min_variance():
upper_assets=np.ones(2),
)
problem.solve()
np.testing.assert_almost_equal(
weights.value, np.array([0.875, 0.125, 0.0, 0.0]), decimal=5
)
np.testing.assert_almost_equal(weights.value, np.array([0.875, 0.125, 0.0, 0.0]), decimal=5)

0 comments on commit f0eebea

Please sign in to comment.