pwb-toolbox 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl
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.
- pwb_toolbox/backtest/__init__.py +2 -50
- pwb_toolbox/backtest/base_strategy.py +1 -1
- pwb_toolbox/backtest/engine.py +33 -0
- pwb_toolbox/backtest/ib_connector.py +69 -0
- pwb_toolbox/datasets/__init__.py +7 -4
- pwb_toolbox/performance/__init__.py +123 -0
- pwb_toolbox/performance/metrics.py +465 -0
- pwb_toolbox/performance/plots.py +415 -0
- pwb_toolbox/performance/trade_stats.py +138 -0
- {pwb_toolbox-0.1.7.dist-info → pwb_toolbox-0.1.9.dist-info}/METADATA +62 -2
- pwb_toolbox-0.1.9.dist-info/RECORD +15 -0
- pwb_toolbox/backtest/execution_models/__init__.py +0 -153
- pwb_toolbox/backtest/insight.py +0 -21
- pwb_toolbox/backtest/portfolio_models/__init__.py +0 -290
- pwb_toolbox/backtest/risk_models/__init__.py +0 -175
- pwb_toolbox/backtest/universe_models/__init__.py +0 -183
- pwb_toolbox-0.1.7.dist-info/RECORD +0 -14
- {pwb_toolbox-0.1.7.dist-info → pwb_toolbox-0.1.9.dist-info}/WHEEL +0 -0
- {pwb_toolbox-0.1.7.dist-info → pwb_toolbox-0.1.9.dist-info}/licenses/LICENSE.txt +0 -0
- {pwb_toolbox-0.1.7.dist-info → pwb_toolbox-0.1.9.dist-info}/top_level.txt +0 -0
@@ -1,183 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from abc import ABC, abstractmethod
|
4
|
-
from datetime import date
|
5
|
-
from typing import Callable, Dict, Iterable, List, Sequence
|
6
|
-
|
7
|
-
import pandas as pd
|
8
|
-
|
9
|
-
from ...datasets import load_dataset
|
10
|
-
|
11
|
-
|
12
|
-
class UniverseSelectionModel(ABC):
|
13
|
-
"""Base class for universe selection models."""
|
14
|
-
|
15
|
-
@abstractmethod
|
16
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
17
|
-
"""Return the active list of symbols."""
|
18
|
-
raise NotImplementedError
|
19
|
-
|
20
|
-
|
21
|
-
class ManualUniverseSelectionModel(UniverseSelectionModel):
|
22
|
-
"""Universe defined by a static list of tickers."""
|
23
|
-
|
24
|
-
def __init__(self, symbols: Sequence[str]):
|
25
|
-
self._symbols = list(symbols)
|
26
|
-
|
27
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
28
|
-
return list(self._symbols)
|
29
|
-
|
30
|
-
|
31
|
-
class ScheduledUniverseSelectionModel(UniverseSelectionModel):
|
32
|
-
"""Switch universe based on a schedule of dates."""
|
33
|
-
|
34
|
-
def __init__(self, schedule: Dict[date | str, Sequence[str]]):
|
35
|
-
self.schedule = {
|
36
|
-
(pd.Timestamp(k).date() if not isinstance(k, date) else k): list(v)
|
37
|
-
for k, v in schedule.items()
|
38
|
-
}
|
39
|
-
|
40
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
41
|
-
if not self.schedule:
|
42
|
-
return []
|
43
|
-
dt = pd.Timestamp(as_of or date.today()).date()
|
44
|
-
valid = [d for d in self.schedule if d <= dt]
|
45
|
-
if not valid:
|
46
|
-
return []
|
47
|
-
last = max(valid)
|
48
|
-
return self.schedule[last]
|
49
|
-
|
50
|
-
|
51
|
-
class CoarseFundamentalUniverseSelectionModel(UniverseSelectionModel):
|
52
|
-
"""Universe filtered using coarse fundamental data."""
|
53
|
-
|
54
|
-
def __init__(
|
55
|
-
self,
|
56
|
-
selector: Callable[[pd.DataFrame], Iterable[str]],
|
57
|
-
dataset: str = "Stocks-Quarterly-BalanceSheet",
|
58
|
-
):
|
59
|
-
self.selector = selector
|
60
|
-
self.dataset = dataset
|
61
|
-
|
62
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
63
|
-
df = load_dataset(self.dataset)
|
64
|
-
return list(self.selector(df))
|
65
|
-
|
66
|
-
|
67
|
-
class FineFundamentalUniverseSelectionModel(UniverseSelectionModel):
|
68
|
-
"""Universe filtered using fine fundamental data."""
|
69
|
-
|
70
|
-
def __init__(
|
71
|
-
self,
|
72
|
-
selector: Callable[[pd.DataFrame], Iterable[str]],
|
73
|
-
dataset: str = "Stocks-Quarterly-Earnings",
|
74
|
-
):
|
75
|
-
self.selector = selector
|
76
|
-
self.dataset = dataset
|
77
|
-
|
78
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
79
|
-
df = load_dataset(self.dataset)
|
80
|
-
return list(self.selector(df))
|
81
|
-
|
82
|
-
|
83
|
-
class ETFConstituentsUniverseSelectionModel(UniverseSelectionModel):
|
84
|
-
"""Universe containing constituents of a given ETF."""
|
85
|
-
|
86
|
-
def __init__(self, etf: str):
|
87
|
-
self.etf = etf
|
88
|
-
|
89
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
90
|
-
df = load_dataset("ETF-Constituents")
|
91
|
-
if "etf" in df.columns:
|
92
|
-
col = "etf"
|
93
|
-
else:
|
94
|
-
col = df.columns[0] if df.columns else "etf"
|
95
|
-
if df.empty:
|
96
|
-
return []
|
97
|
-
return list(df[df[col] == self.etf]["symbol"].unique())
|
98
|
-
|
99
|
-
|
100
|
-
class IndexConstituentsUniverseSelectionModel(UniverseSelectionModel):
|
101
|
-
"""Universe of constituents for a specified index."""
|
102
|
-
|
103
|
-
def __init__(self, index: str):
|
104
|
-
self.index = index
|
105
|
-
|
106
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
107
|
-
df = load_dataset("Index-Constituents")
|
108
|
-
if df.empty:
|
109
|
-
return []
|
110
|
-
col = "index" if "index" in df.columns else df.columns[0]
|
111
|
-
return list(df[df[col] == self.index]["symbol"].unique())
|
112
|
-
|
113
|
-
|
114
|
-
class OptionUniverseSelectionModel(UniverseSelectionModel):
|
115
|
-
"""Universe consisting of options for the given underlyings."""
|
116
|
-
|
117
|
-
def __init__(self, underlying_symbols: Sequence[str]):
|
118
|
-
self.underlyings = list(underlying_symbols)
|
119
|
-
|
120
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
121
|
-
return list(self.underlyings)
|
122
|
-
|
123
|
-
|
124
|
-
class ADRUniverseSelectionModel(UniverseSelectionModel):
|
125
|
-
"""Universe of American Depositary Receipts."""
|
126
|
-
|
127
|
-
def __init__(self, dataset: str = "ADR-Listings"):
|
128
|
-
self.dataset = dataset
|
129
|
-
|
130
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
131
|
-
df = load_dataset(self.dataset)
|
132
|
-
if df.empty:
|
133
|
-
return []
|
134
|
-
return list(df["symbol"].unique())
|
135
|
-
|
136
|
-
|
137
|
-
class CryptoUniverseSelectionModel(UniverseSelectionModel):
|
138
|
-
"""Universe built from cryptocurrency tickers."""
|
139
|
-
|
140
|
-
def __init__(self, top_n: int | None = None):
|
141
|
-
self.top_n = top_n
|
142
|
-
|
143
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
144
|
-
df = load_dataset("Cryptocurrencies-Daily-Price")
|
145
|
-
syms = list(dict.fromkeys(df["symbol"]))
|
146
|
-
if self.top_n is not None:
|
147
|
-
syms = syms[: self.top_n]
|
148
|
-
return syms
|
149
|
-
|
150
|
-
|
151
|
-
class UniverseSelectionModelChain(UniverseSelectionModel):
|
152
|
-
"""Combine multiple universe selection models."""
|
153
|
-
|
154
|
-
def __init__(self, models: Iterable[UniverseSelectionModel]):
|
155
|
-
self.models = list(models)
|
156
|
-
|
157
|
-
def symbols(self, as_of: date | str | None = None) -> List[str]:
|
158
|
-
all_syms: List[str] = []
|
159
|
-
for m in self.models:
|
160
|
-
all_syms.extend(m.symbols(as_of))
|
161
|
-
seen = set()
|
162
|
-
uniq = []
|
163
|
-
for s in all_syms:
|
164
|
-
if s not in seen:
|
165
|
-
seen.add(s)
|
166
|
-
uniq.append(s)
|
167
|
-
return uniq
|
168
|
-
|
169
|
-
|
170
|
-
__all__ = [
|
171
|
-
"UniverseSelectionModel",
|
172
|
-
"ManualUniverseSelectionModel",
|
173
|
-
"ScheduledUniverseSelectionModel",
|
174
|
-
"CoarseFundamentalUniverseSelectionModel",
|
175
|
-
"FineFundamentalUniverseSelectionModel",
|
176
|
-
"ETFConstituentsUniverseSelectionModel",
|
177
|
-
"IndexConstituentsUniverseSelectionModel",
|
178
|
-
"OptionUniverseSelectionModel",
|
179
|
-
"ADRUniverseSelectionModel",
|
180
|
-
"CryptoUniverseSelectionModel",
|
181
|
-
"UniverseSelectionModelChain",
|
182
|
-
]
|
183
|
-
|
@@ -1,14 +0,0 @@
|
|
1
|
-
pwb_toolbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
pwb_toolbox/backtest/__init__.py,sha256=PnyGN0ZF2Apc1yPxQPHEAE7OtEvMza63zZqJPJTyIAg,1868
|
3
|
-
pwb_toolbox/backtest/base_strategy.py,sha256=PQTO9vytnxeDplmaDUC8ORYwo9dTUbwhNrrmHlpDAAU,994
|
4
|
-
pwb_toolbox/backtest/insight.py,sha256=NPrNr7ToNUpqHvgOjgtsP1g8p1Pn8yXuD6YSO-zYePg,394
|
5
|
-
pwb_toolbox/backtest/execution_models/__init__.py,sha256=kMa-C7DPeCwB81pyOp3gjIUSYpI3EuCn1uO9vLTJK4Q,5996
|
6
|
-
pwb_toolbox/backtest/portfolio_models/__init__.py,sha256=VDDDOUhu4kPxYJsOb9dH-qHTfM-Hj8O7hmzLXGuSxs8,9353
|
7
|
-
pwb_toolbox/backtest/risk_models/__init__.py,sha256=Sbd4CeGGhxRFQfdsiMoL7ws-1NJq6IkhxQhXAnGacpY,6319
|
8
|
-
pwb_toolbox/backtest/universe_models/__init__.py,sha256=-NXd_dhPKHgfBpynWjKJ4YxHLvagNhNPfU_JUreK7fc,5715
|
9
|
-
pwb_toolbox/datasets/__init__.py,sha256=3TnI0mcjJywvkKbUdQ-dahD0Py7fjna7lG9cv07vGMg,22259
|
10
|
-
pwb_toolbox-0.1.7.dist-info/licenses/LICENSE.txt,sha256=_Wjz7o7St3iVSPBRzE0keS8XSqSJ03A3NZ6cMlTaSK8,1079
|
11
|
-
pwb_toolbox-0.1.7.dist-info/METADATA,sha256=wgpnREqh2IhIP7TOmAfkzznrXj-TNs-tzyPV-i1Xwoo,5237
|
12
|
-
pwb_toolbox-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
-
pwb_toolbox-0.1.7.dist-info/top_level.txt,sha256=TZcXcF2AMkKkibZOuq6AYsHjajPgddHAGjQUT64OYGY,12
|
14
|
-
pwb_toolbox-0.1.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|