Coverage for cvx/simulator/utils/month.py: 100%
22 statements
« prev ^ index » next coverage.py v7.6.8, created at 2025-01-10 14:11 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2025-01-10 14:11 +0000
1# Copyright 2023 Stanford University Convex Optimization Group
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# # Copyright 2023 Thomas Schmelzer
15# #
16# # Licensed under the Apache License, Version 2.0 (the "License");
17# # you may not use this file except in compliance with the License.
18# # You may obtain a copy of the License at
19# #
20# # http://www.apache.org/licenses/LICENSE-2.0
21# #
22# # Unless required by applicable law or agreed to in writing, software
23# # distributed under the License is distributed on an "AS IS" BASIS,
24# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25# # See the License for the specific language governing permissions and
26# # limitations under the License.
27"""
28Popular year vs month performance table.
30Supports compounded and cumulative (i.e. fixed AUM) returns logic.
31"""
33from __future__ import annotations
35import calendar
36from enum import Enum
38import numpy as np
39import pandas as pd
42def _compound_returns(returns):
43 return (1.0 + returns).prod() - 1.0
46def _cumulative_returns(returns):
47 return returns.sum()
50class Aggregate(Enum):
51 COMPOUND = _compound_returns
52 CUMULATIVE = _cumulative_returns
55def monthlytable(returns: pd.Series, f: Aggregate) -> pd.DataFrame:
56 """
57 Get a table of monthly returns.
59 Args:
60 returns: Series of individual returns.
61 f: Aggregate function to use.
63 Returns:
64 DataFrame with monthly returns, their STDev and YTD.
65 """
66 # Works better in the first month
67 # Compute all the intramonth-returns
68 # instead of reapplying some monthly resampling of the NAV
69 r = pd.Series(returns)
71 return_monthly = r.groupby([r.index.year, r.index.month]).apply(f)
73 frame = return_monthly.unstack(level=1).rename(columns=lambda x: calendar.month_abbr[x])
75 ytd = frame.apply(f, axis=1)
76 frame["STDev"] = np.sqrt(12) * frame.std(axis=1)
77 # make sure that you don't include the column for the STDev in your computation
78 frame["YTD"] = ytd
79 frame.index.name = "Year"
80 frame.columns.name = None
81 # most recent years on top
82 return frame.iloc[::-1]