Skip to content

Commit

Permalink
105 experiment with python fire (#107)
Browse files Browse the repository at this point in the history
* fire in pyproject

* weather example with fire

* wrote test for the fire application

* wrote test for the fire application

* remove the click based weather cli

* move pyyaml out of 1st class dependencies

* serialize into problem class

* remove smallest eigenvalue cli

* remove smallest eigenvalue cli

* move from fff to main

* switch minvariance.py to fire
  • Loading branch information
tschm authored Jul 25, 2023
1 parent e145d69 commit 3ec9692
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 309 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ coverage: ## test and coverage
xdg-open htmlcov/index.html 2> /dev/null; \
fi

.PHONY: tree
tree: install ## make a tree
@poetry show --tree

.PHONY: help
help: ## Display this help screen
Expand Down
12 changes: 0 additions & 12 deletions cvx/cli/aux/problem.py

This file was deleted.

44 changes: 16 additions & 28 deletions cvx/cli/minvariance.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,15 @@
# -*- coding: utf-8 -*-
import traceback

import click
import fire

from cvx.cli.aux.io import exists
from cvx.cli.aux.json import read_json
from cvx.cli.aux.problem import deserialize_problem, serialize_problem
from cvx.markowitz.builder import deserialize
from cvx.markowitz.portfolios.min_var import MinVar, estimate_dimensions


@click.command()
@click.argument("json_file", type=click.Path(exists=True, dir_okay=False))
@click.argument("problem_file", type=click.Path(dir_okay=False), required=False)
@click.option(
"--assets",
"-a",
default=None,
type=int,
required=False,
help="Number of assets",
)
@click.option(
"--factors",
"-f ",
default=None,
type=int,
required=False,
help="Number of factors",
)
def minvariance(json_file, problem_file=None, assets=None, factors=None) -> None:
def cli(json_file, problem_file=None, assets=None, factors=None) -> None:
# parse the json file with input data for the problem
try:
input_data = dict(read_json(json_file))
Expand All @@ -37,7 +18,7 @@ def minvariance(json_file, problem_file=None, assets=None, factors=None) -> None

if does_exist:
# the problem has been serialized before and we can reuse it
problem = deserialize_problem(problem_file)
problem = deserialize(problem_file)

else:
# build the problem from scratch
Expand All @@ -47,20 +28,27 @@ def minvariance(json_file, problem_file=None, assets=None, factors=None) -> None
problem = MinVar(assets=assets, factors=factors).build()
else:
assets, factors = estimate_dimensions(**input_data)
click.echo(
print(
f"Estimated the numbers of assets as {assets} and factors as {factors}"
)
problem = MinVar(assets=assets, factors=factors).build()

# We have constructed the problem, write it to file if the file has been specified
if problem_file is not None:
click.echo(f"Serializing problem in {problem_file}")
serialize_problem(problem, problem_file)
print(f"Serializing problem in {problem_file}")
problem.serialize(problem_file)

problem.update(**input_data)
problem.solve()
click.echo(f"Solution: {problem.weights}")
return problem.weights

except Exception as e:
click.echo(traceback.print_exception(type(e), e, e.__traceback__))
print(traceback.print_exception(type(e), e, e.__traceback__))
raise e


def main(): # pragma: no cover
"""
Run the CLI using Fire
"""
fire.Fire(cli)
23 changes: 0 additions & 23 deletions cvx/cli/smallest_ev.py

This file was deleted.

35 changes: 0 additions & 35 deletions cvx/cli/weather.py

This file was deleted.

37 changes: 37 additions & 0 deletions cvx/cli/weather_fire.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
import fire
import requests


def cli(metric: str, latitude: float = 37.4419, longitude: float = -122.143) -> None:
"""
Get the current weather for a given metric
Parameters
----------
metric : str
The metric to get the current weather for
latitude : float, optional
The latitude to get the current weather for, by default 37.4419
longitude : float, optional
The longitude to get the current weather for, by default -122.143
"""
url = "https://api.open-meteo.com/v1/forecast"
url = f"{url}?latitude={str(latitude)}&longitude={str(longitude)}&current_weather=true"
r = requests.get(url)

if r.status_code == 200:
if metric in r.json()["current_weather"]:
x = r.json()["current_weather"][metric]
return x
else:
raise ValueError("Metric not supported!")
else:
raise ConnectionError("Open-Meteo is down!")


def main(): # pragma: no cover
"""
Run the CLI using Fire
"""
fire.Fire(cli)
10 changes: 10 additions & 0 deletions cvx/markowitz/builder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import annotations

import pickle
from abc import abstractmethod
from dataclasses import dataclass, field
from typing import Dict
Expand All @@ -15,6 +16,11 @@
from cvx.markowitz.risk import FactorModel, SampleCovariance


def deserialize(problem_file):
with open(problem_file, "rb") as infile:
return pickle.load(infile)


@dataclass(frozen=True)
class _Problem:
problem: cp.Problem
Expand Down Expand Up @@ -77,6 +83,10 @@ def weights(self):
def factor_weights(self):
return self.variables[D.FACTOR_WEIGHTS].value

def serialize(self, problem_file):
with open(problem_file, "wb") as outfile:
pickle.dump(self, outfile)


@dataclass(frozen=True)
class Builder:
Expand Down
Loading

0 comments on commit 3ec9692

Please sign in to comment.