diff --git a/cvx/simulator/portfolio.py b/cvx/simulator/portfolio.py index e1cb2008..21a351d5 100644 --- a/cvx/simulator/portfolio.py +++ b/cvx/simulator/portfolio.py @@ -15,7 +15,6 @@ from dataclasses import dataclass from datetime import datetime -from math import sqrt from typing import Any import numpy as np @@ -396,10 +395,13 @@ def snapshot( return fig - def sharpe(self, n=252): + def sharpe(self, n=None): """Simple Sharpe ratio""" + ts = self.nav.pct_change().dropna() - return ts.mean() / ts.std() * sqrt(n) + return sharpe(ts, n=n) + + # return ts.mean() / ts.std() * sqrt(n) @classmethod def from_cashpos_prices( diff --git a/cvx/simulator/utils/metric.py b/cvx/simulator/utils/metric.py index 73e240c6..75f9a01a 100644 --- a/cvx/simulator/utils/metric.py +++ b/cvx/simulator/utils/metric.py @@ -1,8 +1,19 @@ from math import sqrt +import pandas as pd -def sharpe(ts, n=252): + +def _periods(ts): + """ + compute the number of periods in a time series + """ + series = pd.Series(data=ts.index) + return 365 * 24 * 60 * 60 / (series.diff().mean().total_seconds()) + + +def sharpe(ts, n=None): """ compute the sharpe ratio of a time series """ + n = n or _periods(ts) return ts.mean() / ts.std() * sqrt(n) diff --git a/tests/conftest.py b/tests/conftest.py index bf728169..726a581a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,3 +19,13 @@ def prices(resource_dir): return pd.read_csv( resource_dir / "price.csv", index_col=0, parse_dates=True, header=0 ).ffill() + + +@pytest.fixture() +def nav(): + return pd.read_csv( + Path(__file__).parent / "resources" / "nav.csv", + index_col=0, + parse_dates=True, + header=0, + ).squeeze() diff --git a/tests/test_applications/test_talk/test_experiment1.py b/tests/test_applications/test_talk/test_experiment1.py index 1defb400..2b9ca2dd 100644 --- a/tests/test_applications/test_talk/test_experiment1.py +++ b/tests/test_applications/test_talk/test_experiment1.py @@ -29,5 +29,5 @@ def test_portfolio(prices): prices=prices, cashposition=1e6 * f(prices), aum=1e6 ) assert sharpe(portfolio.nav.pct_change().dropna()) == pytest.approx( - 0.5285239056785145 + 0.5375955959644553 ) diff --git a/tests/test_applications/test_talk/test_experiment2.py b/tests/test_applications/test_talk/test_experiment2.py index 07a15fb3..e5e4eb5c 100644 --- a/tests/test_applications/test_talk/test_experiment2.py +++ b/tests/test_applications/test_talk/test_experiment2.py @@ -29,4 +29,4 @@ def test_portfolio(prices): portfolio = Portfolio.from_cashpos_prices( prices=prices, cashposition=1e6 * f(prices), aum=1e8 ) - assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.6132309781658799) + assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.6237309693693432) diff --git a/tests/test_applications/test_talk/test_experiment3.py b/tests/test_applications/test_talk/test_experiment3.py index 72430972..597311cf 100644 --- a/tests/test_applications/test_talk/test_experiment3.py +++ b/tests/test_applications/test_talk/test_experiment3.py @@ -30,4 +30,4 @@ def test_portfolio(prices): portfolio = Portfolio.from_cashpos_prices( prices=prices, cashposition=1e6 * f(prices), aum=1e8 ) - assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.8999500486718532) + assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.9153593608740396) diff --git a/tests/test_applications/test_talk/test_experiment4.py b/tests/test_applications/test_talk/test_experiment4.py index f5ee5567..d15dff4e 100644 --- a/tests/test_applications/test_talk/test_experiment4.py +++ b/tests/test_applications/test_talk/test_experiment4.py @@ -40,4 +40,4 @@ def test_portfolio(prices): portfolio = Portfolio.from_cashpos_prices( prices=prices, cashposition=1e6 * f(prices), aum=1e8 ) - assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.9679528514122532) + assert sharpe(portfolio.nav.pct_change()) == pytest.approx(0.98452653536996) diff --git a/tests/test_applications/test_talk/test_experiment5.py b/tests/test_applications/test_talk/test_experiment5.py index d1819631..54a3b232 100644 --- a/tests/test_applications/test_talk/test_experiment5.py +++ b/tests/test_applications/test_talk/test_experiment5.py @@ -48,7 +48,7 @@ def test_portfolio(prices): portfolio = builder.build() assert sharpe(portfolio.nav.pct_change().dropna()) == pytest.approx( - 1.3163233581460896 + 1.338916996187723 ) # portfolio.metrics()["Sharpe"] == pytest.approx(1.2778671597915794) diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index d296bed1..391f7f99 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -1,20 +1,17 @@ -from pathlib import Path - import pandas as pd import pytest from cvx.simulator.portfolio import Portfolio from cvx.simulator.utils.metric import sharpe - -@pytest.fixture() -def nav(): - return pd.read_csv( - Path(__file__).parent / "resources" / "nav.csv", - index_col=0, - parse_dates=True, - header=0, - ).squeeze() +# @pytest.fixture() +# def nav(): +# return pd.read_csv( +# Path(__file__).parent / "resources" / "nav.csv", +# index_col=0, +# parse_dates=True, +# header=0, +# ).squeeze() @pytest.fixture() @@ -182,4 +179,4 @@ def test_profit_metrics(portfolio): assert portfolio.profit.sum() == pytest.approx(-3492.4000000000033) assert sharpe(portfolio.profit, n=252) == pytest.approx(-0.10965282385614909) # profit is replacing NaNs with 0?! - assert portfolio.sharpe() == pytest.approx(-0.10210959124482079) + assert portfolio.sharpe() == pytest.approx(-0.1038600869081656) diff --git a/tests/test_utils/test_metric.py b/tests/test_utils/test_metric.py new file mode 100644 index 00000000..d57079b9 --- /dev/null +++ b/tests/test_utils/test_metric.py @@ -0,0 +1,9 @@ +import pytest + +from cvx.simulator.utils.metric import sharpe + + +def test_sharpe(nav): + s = sharpe(nav.pct_change()) + print(s) + assert s == pytest.approx(-0.10388478316042028) diff --git a/tests/test_utils/test_month.py b/tests/test_utils/test_month.py index d89ece45..376d5a9a 100644 --- a/tests/test_utils/test_month.py +++ b/tests/test_utils/test_month.py @@ -1,17 +1,6 @@ -import pandas as pd -import pytest - from cvx.simulator.utils.month import Aggregate, monthlytable -@pytest.fixture() -def nav(resource_dir): - """nav fixture""" - return pd.read_csv( - resource_dir / "nav.csv", index_col=0, parse_dates=True, header=0 - ).squeeze() - - def test_month_compounded(nav): table = monthlytable(nav.pct_change().fillna(0.0), Aggregate.COMPOUND) print(table)