openseries 1.8.2__tar.gz → 1.8.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openseries-1.8.2 → openseries-1.8.3}/PKG-INFO +4 -3
- {openseries-1.8.2 → openseries-1.8.3}/README.md +3 -2
- {openseries-1.8.2 → openseries-1.8.3}/openseries/_common_model.py +173 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/owntypes.py +9 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/plotly_layouts.json +1 -1
- {openseries-1.8.2 → openseries-1.8.3}/pyproject.toml +4 -4
- {openseries-1.8.2 → openseries-1.8.3}/LICENSE.md +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/__init__.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/_risk.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/datefixer.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/frame.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/load_plotly.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/plotly_captor_logo.json +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/portfoliotools.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/series.py +0 -0
- {openseries-1.8.2 → openseries-1.8.3}/openseries/simulation.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: openseries
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.3
|
4
4
|
Summary: Tools for analyzing financial timeseries.
|
5
5
|
License: # BSD 3-Clause License
|
6
6
|
|
@@ -330,8 +330,9 @@ make lint
|
|
330
330
|
| `value_to_diff` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a series of differences. |
|
331
331
|
| `value_to_log` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a logarithmic return series. |
|
332
332
|
| `value_ret_calendar_period` | `OpenTimeSeries`, `OpenFrame` | Returns the series simple return for a specific calendar period. |
|
333
|
-
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the
|
334
|
-
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the
|
333
|
+
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the serie(s) in a browser window. |
|
334
|
+
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the serie(s) in a browser window. |
|
335
|
+
| `plot_histogram` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Histogram](https://plotly.com/python/histograms/) plot of the serie(s) in a browser window. |
|
335
336
|
| `to_drawdown_series` | `OpenTimeSeries`, `OpenFrame` | Converts the series into drawdown series. |
|
336
337
|
| `rolling_return` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling returns. |
|
337
338
|
| `rolling_vol` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling volatilities. |
|
@@ -265,8 +265,9 @@ make lint
|
|
265
265
|
| `value_to_diff` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a series of differences. |
|
266
266
|
| `value_to_log` | `OpenTimeSeries`, `OpenFrame` | Converts a value series into a logarithmic return series. |
|
267
267
|
| `value_ret_calendar_period` | `OpenTimeSeries`, `OpenFrame` | Returns the series simple return for a specific calendar period. |
|
268
|
-
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the
|
269
|
-
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the
|
268
|
+
| `plot_series` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Scatter](https://plotly.com/python/line-and-scatter/) plot of the serie(s) in a browser window. |
|
269
|
+
| `plot_bars` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Bar](https://plotly.com/python/bar-charts/) plot of the serie(s) in a browser window. |
|
270
|
+
| `plot_histogram` | `OpenTimeSeries`, `OpenFrame` | Opens a HTML [Plotly Histogram](https://plotly.com/python/histograms/) plot of the serie(s) in a browser window. |
|
270
271
|
| `to_drawdown_series` | `OpenTimeSeries`, `OpenFrame` | Converts the series into drawdown series. |
|
271
272
|
| `rolling_return` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling returns. |
|
272
273
|
| `rolling_vol` | `OpenTimeSeries`, `OpenFrame` | Returns a pandas.DataFrame with rolling volatilities. |
|
@@ -33,6 +33,10 @@ if TYPE_CHECKING: # pragma: no cover
|
|
33
33
|
LiteralLinePlotMode,
|
34
34
|
LiteralNanMethod,
|
35
35
|
LiteralPandasReindexMethod,
|
36
|
+
LiteralPlotlyHistogramBarMode,
|
37
|
+
LiteralPlotlyHistogramCurveType,
|
38
|
+
LiteralPlotlyHistogramHistNorm,
|
39
|
+
LiteralPlotlyHistogramPlotType,
|
36
40
|
LiteralPlotlyJSlib,
|
37
41
|
LiteralPlotlyOutput,
|
38
42
|
LiteralQuantileInterp,
|
@@ -50,6 +54,7 @@ from pandas import (
|
|
50
54
|
to_datetime,
|
51
55
|
)
|
52
56
|
from pandas.tseries.offsets import CustomBusinessDay
|
57
|
+
from plotly.figure_factory import create_distplot # type: ignore[import-untyped]
|
53
58
|
from plotly.graph_objs import Figure # type: ignore[import-untyped]
|
54
59
|
from plotly.io import to_html # type: ignore[import-untyped]
|
55
60
|
from plotly.offline import plot # type: ignore[import-untyped]
|
@@ -1005,6 +1010,174 @@ class _CommonModel(BaseModel): # type: ignore[misc]
|
|
1005
1010
|
|
1006
1011
|
return figure, string_output
|
1007
1012
|
|
1013
|
+
def plot_histogram(
|
1014
|
+
self: Self,
|
1015
|
+
plot_type: LiteralPlotlyHistogramPlotType = "bars",
|
1016
|
+
histnorm: LiteralPlotlyHistogramHistNorm = "percent",
|
1017
|
+
barmode: LiteralPlotlyHistogramBarMode = "overlay",
|
1018
|
+
xbins_size: float | None = None,
|
1019
|
+
opacity: float = 0.75,
|
1020
|
+
bargap: float = 0.0,
|
1021
|
+
bargroupgap: float = 0.0,
|
1022
|
+
curve_type: LiteralPlotlyHistogramCurveType = "kde",
|
1023
|
+
x_fmt: str | None = None,
|
1024
|
+
y_fmt: str | None = None,
|
1025
|
+
filename: str | None = None,
|
1026
|
+
directory: DirectoryPath | None = None,
|
1027
|
+
labels: list[str] | None = None,
|
1028
|
+
output_type: LiteralPlotlyOutput = "file",
|
1029
|
+
include_plotlyjs: LiteralPlotlyJSlib = "cdn",
|
1030
|
+
*,
|
1031
|
+
cumulative: bool = False,
|
1032
|
+
show_rug: bool = False,
|
1033
|
+
auto_open: bool = True,
|
1034
|
+
add_logo: bool = True,
|
1035
|
+
) -> tuple[Figure, str]:
|
1036
|
+
"""Create a Plotly Histogram Figure.
|
1037
|
+
|
1038
|
+
Parameters
|
1039
|
+
----------
|
1040
|
+
plot_type: LiteralPlotlyHistogramPlotType, default: bars
|
1041
|
+
Type of plot
|
1042
|
+
histnorm: LiteralPlotlyHistogramHistNorm, default: percent
|
1043
|
+
Sets the normalization mode
|
1044
|
+
barmode: LiteralPlotlyHistogramBarMode, default: overlay
|
1045
|
+
Specifies how bar traces are displayed relative to one another
|
1046
|
+
xbins_size: float, optional
|
1047
|
+
Explicitly sets the width of each bin along the x-axis in data units
|
1048
|
+
opacity: float, default: 0.75
|
1049
|
+
Sets the trace opacity, must be between 0 (fully transparent) and 1
|
1050
|
+
bargap: float, default: 0.0
|
1051
|
+
Sets the gap between bars of adjacent location coordinates
|
1052
|
+
bargroupgap: float, default: 0.0
|
1053
|
+
Sets the gap between bar “groups” at the same location coordinate
|
1054
|
+
curve_type: LiteralPlotlyHistogramCurveType, default: kde
|
1055
|
+
Specifies the type of distribution curve to overlay on the histogram
|
1056
|
+
y_fmt: str, optional
|
1057
|
+
None, '%', '.1%' depending on number of decimals to show on the y-axis
|
1058
|
+
x_fmt: str, optional
|
1059
|
+
None, '%', '.1%' depending on number of decimals to show on the x-axis
|
1060
|
+
filename: str, optional
|
1061
|
+
Name of the Plotly html file
|
1062
|
+
directory: DirectoryPath, optional
|
1063
|
+
Directory where Plotly html file is saved
|
1064
|
+
labels: list[str], optional
|
1065
|
+
A list of labels to manually override using the names of
|
1066
|
+
the input self.tsdf
|
1067
|
+
output_type: LiteralPlotlyOutput, default: "file"
|
1068
|
+
Determines output type
|
1069
|
+
include_plotlyjs: LiteralPlotlyJSlib, default: "cdn"
|
1070
|
+
Determines how the plotly.js library is included in the output
|
1071
|
+
cumulative: bool, default: False
|
1072
|
+
Determines whether to compute a cumulative histogram
|
1073
|
+
show_rug: bool, default: False
|
1074
|
+
Determines whether to draw a rug plot alongside the distribution
|
1075
|
+
auto_open: bool, default: True
|
1076
|
+
Determines whether to open a browser window with the plot
|
1077
|
+
add_logo: bool, default: True
|
1078
|
+
If True a Captor logo is added to the plot
|
1079
|
+
|
1080
|
+
Returns:
|
1081
|
+
-------
|
1082
|
+
tuple[plotly.go.Figure, str]
|
1083
|
+
Plotly Figure and a div section or a html filename with location
|
1084
|
+
|
1085
|
+
"""
|
1086
|
+
if labels:
|
1087
|
+
if len(labels) != self.tsdf.shape[1]:
|
1088
|
+
msg = "Must provide same number of labels as items in frame."
|
1089
|
+
raise NumberOfItemsAndLabelsNotSameError(msg)
|
1090
|
+
else:
|
1091
|
+
labels = list(self.tsdf.columns.get_level_values(0))
|
1092
|
+
|
1093
|
+
if directory:
|
1094
|
+
dirpath = Path(directory).resolve()
|
1095
|
+
elif Path.home().joinpath("Documents").exists():
|
1096
|
+
dirpath = Path.home().joinpath("Documents")
|
1097
|
+
else:
|
1098
|
+
dirpath = Path(stack()[1].filename).parent
|
1099
|
+
|
1100
|
+
if not filename:
|
1101
|
+
filename = "".join(choice(ascii_letters) for _ in range(6)) + ".html"
|
1102
|
+
plotfile = dirpath.joinpath(filename)
|
1103
|
+
|
1104
|
+
fig_dict, logo = load_plotly_dict()
|
1105
|
+
|
1106
|
+
hovertemplate = f"Count: %{{y:{y_fmt}}}" if y_fmt else "Count: %{y}"
|
1107
|
+
|
1108
|
+
if x_fmt:
|
1109
|
+
hovertemplate += f"<br>%{{x:{x_fmt}}}"
|
1110
|
+
else:
|
1111
|
+
hovertemplate += "<br>%{x}"
|
1112
|
+
|
1113
|
+
msg = "plot_type must be 'bars' or 'lines'."
|
1114
|
+
if plot_type == "bars":
|
1115
|
+
figure = Figure(fig_dict)
|
1116
|
+
for item in range(self.tsdf.shape[1]):
|
1117
|
+
figure.add_histogram(
|
1118
|
+
x=self.tsdf.iloc[:, item],
|
1119
|
+
cumulative={"enabled": cumulative},
|
1120
|
+
histnorm=histnorm,
|
1121
|
+
name=labels[item],
|
1122
|
+
xbins={"size": xbins_size},
|
1123
|
+
opacity=opacity,
|
1124
|
+
hovertemplate=hovertemplate,
|
1125
|
+
)
|
1126
|
+
figure.update_layout(
|
1127
|
+
barmode=barmode,
|
1128
|
+
bargap=bargap,
|
1129
|
+
bargroupgap=bargroupgap,
|
1130
|
+
)
|
1131
|
+
elif plot_type == "lines":
|
1132
|
+
hist_data = [
|
1133
|
+
cast("Series[float]", self.tsdf.loc[:, ds]).dropna().tolist()
|
1134
|
+
for ds in self.tsdf
|
1135
|
+
]
|
1136
|
+
figure = create_distplot(
|
1137
|
+
hist_data=hist_data,
|
1138
|
+
curve_type=curve_type,
|
1139
|
+
group_labels=labels,
|
1140
|
+
show_hist=False,
|
1141
|
+
show_rug=show_rug,
|
1142
|
+
histnorm=histnorm,
|
1143
|
+
)
|
1144
|
+
figure.update_layout(dict1=fig_dict["layout"])
|
1145
|
+
else:
|
1146
|
+
raise TypeError(msg)
|
1147
|
+
|
1148
|
+
figure.update_layout(xaxis={"tickformat": x_fmt}, yaxis={"tickformat": y_fmt})
|
1149
|
+
|
1150
|
+
figure.update_xaxes(zeroline=True, zerolinewidth=2, zerolinecolor="lightgrey")
|
1151
|
+
figure.update_yaxes(zeroline=True, zerolinewidth=2, zerolinecolor="lightgrey")
|
1152
|
+
|
1153
|
+
if add_logo:
|
1154
|
+
figure.add_layout_image(logo)
|
1155
|
+
|
1156
|
+
if output_type == "file":
|
1157
|
+
plot(
|
1158
|
+
figure_or_data=figure,
|
1159
|
+
filename=str(plotfile),
|
1160
|
+
auto_open=auto_open,
|
1161
|
+
auto_play=False,
|
1162
|
+
link_text="",
|
1163
|
+
include_plotlyjs=cast("bool", include_plotlyjs),
|
1164
|
+
config=fig_dict["config"],
|
1165
|
+
output_type=output_type,
|
1166
|
+
)
|
1167
|
+
string_output = str(plotfile)
|
1168
|
+
else:
|
1169
|
+
div_id = filename.rsplit(".", 1)[0]
|
1170
|
+
string_output = to_html(
|
1171
|
+
fig=figure,
|
1172
|
+
config=fig_dict["config"],
|
1173
|
+
auto_play=False,
|
1174
|
+
include_plotlyjs=cast("bool", include_plotlyjs),
|
1175
|
+
full_html=False,
|
1176
|
+
div_id=div_id,
|
1177
|
+
)
|
1178
|
+
|
1179
|
+
return figure, string_output
|
1180
|
+
|
1008
1181
|
def arithmetic_ret_func(
|
1009
1182
|
self: Self,
|
1010
1183
|
months_from_last: int | None = None,
|
@@ -135,6 +135,15 @@ LiteralCaptureRatio = Literal["up", "down", "both"]
|
|
135
135
|
LiteralBarPlotMode = Literal["stack", "group", "overlay", "relative"]
|
136
136
|
LiteralPlotlyOutput = Literal["file", "div"]
|
137
137
|
LiteralPlotlyJSlib = Literal[True, False, "cdn"]
|
138
|
+
LiteralPlotlyHistogramPlotType = Literal["bars", "lines"]
|
139
|
+
LiteralPlotlyHistogramBarMode = Literal["stack", "group", "overlay", "relative"]
|
140
|
+
LiteralPlotlyHistogramCurveType = Literal["normal", "kde"]
|
141
|
+
LiteralPlotlyHistogramHistNorm = Literal[
|
142
|
+
"percent",
|
143
|
+
"probability",
|
144
|
+
"density",
|
145
|
+
"probability density",
|
146
|
+
]
|
138
147
|
LiteralOlsFitMethod = Literal["pinv", "qr"]
|
139
148
|
LiteralPortfolioWeightings = Literal["eq_weights", "inv_vol"]
|
140
149
|
LiteralOlsFitCovType = Literal[
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "openseries"
|
3
|
-
version = "1.8.
|
3
|
+
version = "1.8.3"
|
4
4
|
description = "Tools for analyzing financial timeseries."
|
5
5
|
authors = [
|
6
6
|
{ name = "Martin Karrin", email = "martin.karrin@captor.se" },
|
@@ -67,13 +67,13 @@ mypy = "1.15.0"
|
|
67
67
|
pandas-stubs = ">=2.1.2,<3.0.0"
|
68
68
|
pre-commit = ">=3.7.1,<6.0.0"
|
69
69
|
pytest = ">=8.2.2,<9.0.0"
|
70
|
-
ruff = "0.11.
|
70
|
+
ruff = "0.11.9"
|
71
71
|
types-openpyxl = ">=3.1.2,<5.0.0"
|
72
72
|
types-python-dateutil = ">=2.8.2,<4.0.0"
|
73
73
|
types-requests = ">=2.20.0,<3.0.0"
|
74
74
|
|
75
75
|
[build-system]
|
76
|
-
requires = ["poetry-core>=2.1.
|
76
|
+
requires = ["poetry-core>=2.1.3"]
|
77
77
|
build-backend = "poetry.core.masonry.api"
|
78
78
|
|
79
79
|
[tool.coverage.run]
|
@@ -131,7 +131,7 @@ ignore = ["COM812", "D203", "D213"]
|
|
131
131
|
fixable = ["ALL"]
|
132
132
|
mccabe = { max-complexity = 15 }
|
133
133
|
pydocstyle = { convention = "google" }
|
134
|
-
pylint = { max-args =
|
134
|
+
pylint = { max-args = 19, max-branches = 23, max-statements = 59 }
|
135
135
|
|
136
136
|
[tool.pytest.ini_options]
|
137
137
|
testpaths = "tests"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|