Skip to content

Commit 6eca223

Browse files
committed
Add more docs
Add docstrings for cash function/class Fix doc link fix type Fix formatting for CI
1 parent fa31a2b commit 6eca223

File tree

10 files changed

+130
-44
lines changed

10 files changed

+130
-44
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
========
2+
Decision
3+
========
4+
5+
.. currentmodule:: backtest_lib
6+
7+
Decision Type
8+
-------------
9+
10+
.. currentmodule:: backtest_lib.engine.decision
11+
12+
.. autosummary::
13+
:toctree: api/
14+
15+
Decision
16+
17+
Decision Functions
18+
------------------
19+
20+
.. currentmodule:: backtest_lib
21+
22+
.. autosummary::
23+
:toctree: api/
24+
25+
hold
26+
target_holdings
27+
target_weights
28+
trade
29+
reallocate
30+
combine
Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
1-
Decision
2-
========
1+
Decision Types
2+
==============
33

44
.. currentmodule:: backtest_lib.engine.decision
55

6-
Decision Constructors
7-
---------------------
6+
Decision Objects
7+
----------------
88

99
.. autosummary::
1010
:toctree: api/
1111

12-
hold
13-
target_holdings
14-
target_weights
15-
trade
16-
reallocate
17-
combine
18-
19-
Decision Type
20-
-------------
21-
22-
.. autosummary::
23-
:toctree: api/
24-
25-
Decision
12+
DecisionBase
13+
CompositeDecision
14+
HoldDecision
15+
MakeTradeDecision
16+
ReallocateDecision
17+
TargetHoldingsDecision
18+
TargetWeightsDecision
19+
ReallocationMode
20+
TradeDirection

docs/source/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This is an overview of the public API of backtest-lib.
88
:hidden:
99

1010
backtest/index
11+
decision/index
1112
market/index
1213
strategy/index
1314
portfolio/index

src/backtest_lib/backtest/results.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323

2424
@dataclass
2525
class BacktestResults[IndexT: Comparable]:
26-
"""Snapshot of a backtest's results, with key statistics pre-computed.
27-
"""
26+
"""Snapshot of a backtest's results, with key statistics pre-computed."""
2827

2928
periods: Sequence[IndexT] = field(repr=False)
3029
securities: Sequence[str] = field(repr=False)

src/backtest_lib/engine/decision/__init__.py

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99

1010

1111
class ReallocationMode(StrEnum):
12-
"""Modes for allocating out of and into security sets."""
12+
"""Modes for allocating out of and into security sets.
13+
14+
- ``EQUAL_OUT_EQUAL_IN`` sells evenly across ``out_of`` and buys evenly into
15+
``into``.
16+
- ``PRO_RATA_OUT_EQUAL_IN`` sells in proportion to existing weights and buys
17+
evenly into ``into``.
18+
"""
1319

1420
EQUAL_OUT_EQUAL_IN = "equal_out_equal_in"
1521
PRO_RATA_OUT_EQUAL_IN = "pro_rata_out_equal_in"
@@ -30,8 +36,13 @@ class DecisionBase:
3036
form composite decisions.
3137
"""
3238

33-
def __add__(self, other: Decision) -> Decision:
34-
"""Combine two decisions into a composite decision."""
39+
def __add__(self: Decision, other: Decision) -> Decision:
40+
"""Combine two decisions into a composite decision.
41+
42+
``HoldDecision`` acts as the identity element. When either side is a
43+
:class:`HoldDecision`, the other decision is returned. Two composites are
44+
flattened into a single :class:`CompositeDecision`.
45+
"""
3546
if isinstance(self, HoldDecision):
3647
return other
3748
if isinstance(other, HoldDecision):
@@ -43,7 +54,10 @@ def __add__(self, other: Decision) -> Decision:
4354

4455
@dataclass(frozen=True, slots=True)
4556
class MakeTradeDecision(DecisionBase):
46-
"""Decision returned by :func:`~backtest_lib.engine.decision.trade`."""
57+
"""Decision representing an explicit buy or sell instruction.
58+
59+
Use :func:`~backtest_lib.engine.decision.trade` to construct this decision.
60+
"""
4761

4862
direction: TradeDirection
4963
qty: int
@@ -57,14 +71,22 @@ def __post_init__(self):
5771

5872
@dataclass(frozen=True, slots=True)
5973
class CompositeDecision(DecisionBase):
60-
"""Decision returned by :func:`~backtest_lib.engine.decision.combine`."""
74+
"""Decision containing multiple sub-decisions.
75+
76+
Use :func:`~backtest_lib.engine.decision.combine` to construct this decision
77+
or rely on ``+`` to merge decisions.
78+
"""
6179

6280
decisions: tuple[Decision, ...]
6381

6482

6583
@dataclass(frozen=True, slots=True)
6684
class TargetHoldingsDecision(DecisionBase):
67-
"""Decision returned by :func:`~backtest_lib.engine.decision.target_holdings`."""
85+
"""Decision targeting discrete share counts for each security.
86+
87+
Use :func:`~backtest_lib.engine.decision.target_holdings` to construct this
88+
decision.
89+
"""
6890

6991
target_holdings: Mapping[str, int]
7092
fill_cash: bool
@@ -73,7 +95,11 @@ class TargetHoldingsDecision(DecisionBase):
7395

7496
@dataclass(frozen=True, slots=True)
7597
class TargetWeightsDecision(DecisionBase):
76-
"""Decision returned by :func:`~backtest_lib.engine.decision.target_weights`."""
98+
"""Decision targeting portfolio weights for each security.
99+
100+
Use :func:`~backtest_lib.engine.decision.target_weights` to construct this
101+
decision.
102+
"""
77103

78104
target_weights: Mapping[str, float]
79105
fill_cash: bool
@@ -82,14 +108,21 @@ class TargetWeightsDecision(DecisionBase):
82108

83109
@dataclass(frozen=True, slots=True)
84110
class HoldDecision(DecisionBase):
85-
"""Decision returned by :func:`~backtest_lib.engine.decision.hold`."""
111+
"""Decision that leaves the portfolio unchanged.
112+
113+
Use :func:`~backtest_lib.engine.decision.hold` to construct this decision.
114+
"""
86115

87116
pass
88117

89118

90119
@dataclass(frozen=True, slots=True)
91120
class ReallocateDecision(DecisionBase):
92-
"""Decision returned by :func:`~backtest_lib.engine.decision.reallocate`."""
121+
"""Decision to move exposure from one security set to another.
122+
123+
Use :func:`~backtest_lib.engine.decision.reallocate` to construct this
124+
decision.
125+
"""
93126

94127
fraction: float
95128
from_securities: frozenset[str]
@@ -115,7 +148,9 @@ def reallocate(
115148
fraction: Fraction of holdings to reallocate.
116149
out_of: Securities to reduce positions in.
117150
into: Securities to increase positions in.
118-
mode: Allocation mode for distributing sales and buys.
151+
mode: Allocation mode for distributing sales and buys. Accepts
152+
``"equal_out_equal_in"``, ``"pro_rata_out_equal_in"``, or a
153+
:class:`~backtest_lib.engine.decision.ReallocationMode`.
119154
120155
Returns:
121156
ReallocateDecision describing the move.
@@ -153,8 +188,7 @@ def hold() -> HoldDecision:
153188
"""Create a decision that makes no trades.
154189
155190
This is the neutral element of the decision language. When combined with
156-
another decision, the other decision is returned unchanged, mirroring
157-
:func:`~backtest_lib.engine.decision.combine`.
191+
another decision, the other decision is returned unchanged.
158192
"""
159193
return HoldDecision()
160194

@@ -165,7 +199,8 @@ def target_holdings(
165199
"""Create a decision targeting discrete holdings.
166200
167201
This is part of the strategy output language. Use it when your strategy
168-
expresses desired positions as share counts.
202+
expresses desired positions as share counts. The holdings keys must be a
203+
subset of the strategy universe; missing securities are treated as zero.
169204
170205
Args:
171206
holdings: Desired integer holdings per security.
@@ -184,7 +219,9 @@ def target_weights(
184219
"""Create a decision targeting portfolio weights.
185220
186221
This is part of the strategy output language. Use it when your strategy
187-
expresses desired positions as weights that should sum with cash.
222+
expresses desired positions as weights that should sum with cash. The weight
223+
keys must be a subset of the strategy universe; missing securities are
224+
treated as zero.
188225
189226
Args:
190227
weights: Desired weights per security.
@@ -220,10 +257,11 @@ def trade(
220257
221258
This is part of the strategy output language for explicit trades. Use it
222259
when you want a single-buy/sell instruction rather than a target portfolio
223-
expressed via :func:`~backtest_lib.engine.decision.target_weights`.
260+
expressed via :func:`~backtest_lib.target_weights`.
224261
225262
Args:
226-
direction: Buy or sell direction.
263+
direction: Buy or sell direction. Accepts ``"buy"``, ``"sell"``, or a
264+
:class:`~backtest_lib.engine.decision.TradeDirection`.
227265
qty: Quantity to trade.
228266
security: Security identifier to trade.
229267

src/backtest_lib/engine/execute/perfect_world.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,9 @@ def execute_plan(
354354
prices: UniverseMapping,
355355
market: MarketView,
356356
) -> ExecutionResult:
357-
"""Executes a plan with assumptions of perfect conditions i.e no fees or slippage.
358-
Any targetting op will instantly change the portfolio to that target, so the
359-
convention in ``self._normalize_ops`` is to yield the target first.
357+
"""Executes a plan with assumptions of perfect conditions i.e no fees or
358+
slippage. Any targetting op will instantly change the portfolio to that target,
359+
so the convention in ``self._normalize_ops`` is to yield the target first.
360360
"""
361361
del market
362362

src/backtest_lib/engine/plan/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class TradeOrder:
1616
1717
Trade orders are produced by plan generators and executed by a
1818
:class:`~backtest_lib.engine.execute.PlanExecutor`.
19+
``direction`` and ``qty`` together define the signed position change, while
20+
``price`` is the execution price used to compute cash impact.
1921
"""
2022

2123
direction: TradeDirection
@@ -34,7 +36,12 @@ def cost(self) -> float:
3436

3537

3638
class PlanOp:
37-
"""Marker base class for plan operations."""
39+
"""Marker base class for plan operations.
40+
41+
Plan operations are the atomic instructions inside a
42+
:class:`~backtest_lib.engine.plan.Plan` that a
43+
:class:`~backtest_lib.engine.execute.PlanExecutor` can apply.
44+
"""
3845

3946
...
4047

src/backtest_lib/market/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ class ByPeriod[ValueT: (float, int), Index: Comparable](ABC):
222222
"""Period-axis accessor for a :class:`PastView`.
223223
224224
Provides indexing by period position and slicing by range. A single period
225-
access returns a :class:`~backtest_lib.universe.universe_mapping.UniverseMapping`, while
226-
slicing returns a new :class:`PastView`.
225+
access returns a :class:`~backtest_lib.universe.universe_mapping.UniverseMapping`,
226+
while slicing returns a new :class:`PastView`.
227227
"""
228228

229229
@abstractmethod

src/backtest_lib/market/plotting.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ def line(
5151

5252

5353
class UniverseMappingPlotAccessor(ABC):
54-
"""Plot accessor for :class:`~backtest_lib.universe.universe_mapping.UniverseMapping` data.
54+
"""Plot accessor for
55+
:class:`~backtest_lib.universe.universe_mapping.UniverseMapping` data.
5556
5657
Backends implement this interface to visualize a mapping of securities to
5758
scalar values. The accessor is available on ``UniverseMapping.plot``.

src/backtest_lib/portfolio/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ def uniform_portfolio(
296296

297297
@dataclass(frozen=True, slots=True)
298298
class Cash:
299+
"""The type of the cash-only portfolio returned by
300+
:func:`~backtest_lib.portfolio.cash`"""
301+
299302
value: float
300303

301304
def materialize(self, universe: Iterable[str], backend: str) -> WeightedPortfolio:
@@ -312,7 +315,19 @@ def materialize(self, universe: Iterable[str], backend: str) -> WeightedPortfoli
312315
)
313316

314317

315-
def cash(value: float):
318+
def cash(value: float) -> Cash:
319+
"""Create a cash-only portfolio.
320+
321+
This can be used as the ``initial_portfolio`` for a
322+
:class:`~backtest_lib.backtest.Backtest` when you want to begin with no
323+
holdings.
324+
325+
Args:
326+
value: Starting cash value.
327+
328+
Returns:
329+
Cash portfolio with the provided value.
330+
"""
316331
return Cash(value=value)
317332

318333

0 commit comments

Comments
 (0)