Skip to content

Commit c024c4e

Browse files
committed
Support *args and **kwargs in add_execution
1 parent b1fd0ee commit c024c4e

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

src/pybroker/strategy.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,17 @@ class Execution(NamedTuple):
102102
execution of ``fn``.
103103
indicator_names: Names of :class:`pybroker.indicator.Indicator`\ s
104104
used for execution of ``fn``.
105+
args: Additional positional arguments for ``fn``.
106+
kwargs: Additional keyword arguments for ``fn``.
105107
"""
106108

107109
id: int
108110
symbols: frozenset[str]
109111
fn: Optional[Callable[[ExecContext], None]]
110112
model_names: frozenset[str]
111113
indicator_names: frozenset[str]
114+
args: tuple[Any, ...] = tuple()
115+
kwargs: tuple[tuple[str, Any], ...] = tuple()
112116

113117

114118
class BacktestMixin:
@@ -186,6 +190,8 @@ def backtest_executions(
186190
pending_order_scope = PendingOrderScope()
187191
exec_ctxs: dict[str, ExecContext] = {}
188192
exec_fns: dict[str, Callable[[ExecContext], None]] = {}
193+
exec_args: dict[str, tuple[Any, ...]] = {}
194+
exec_kwargs: dict[str, tuple[tuple[str, Any], ...]] = {}
189195
for sym in test_syms:
190196
for exec in executions:
191197
if sym not in exec.symbols:
@@ -203,6 +209,8 @@ def backtest_executions(
203209
sym_end_index=sym_end_index,
204210
session=sessions[sym],
205211
)
212+
exec_args[sym] = exec.args
213+
exec_kwargs[sym] = exec.kwargs
206214
if exec.fn is not None:
207215
exec_fns[sym] = exec.fn
208216
sym_exec_dates = {
@@ -311,7 +319,11 @@ def backtest_executions(
311319
before_exec_fn(active_ctxs)
312320
for sym, ctx in active_ctxs.items():
313321
if sym in exec_fns:
314-
exec_fns[sym](ctx)
322+
exec_fns[sym](
323+
ctx,
324+
*exec_args.get(sym, ()),
325+
**dict(exec_kwargs.get(sym, ())),
326+
)
315327
if after_exec_fn is not None and active_ctxs:
316328
after_exec_fn(active_ctxs)
317329
for ctx in active_ctxs.values():
@@ -896,6 +908,8 @@ def add_execution(
896908
symbols: Union[str, Iterable[str]],
897909
models: Optional[Union[ModelSource, Iterable[ModelSource]]] = None,
898910
indicators: Optional[Union[Indicator, Iterable[Indicator]]] = None,
911+
*args: Any,
912+
**kwargs: Any,
899913
):
900914
r"""Adds an execution to backtest.
901915
@@ -910,6 +924,8 @@ def add_execution(
910924
indicators: :class:`Iterable` of
911925
:class:`pybroker.indicator.Indicator`\ s to compute for
912926
backtesting.
927+
args: Positional arguments passed to ``fn``.
928+
kwargs: Keyword arguments passed to ``fn``.
913929
"""
914930
symbols = (
915931
frozenset((symbols,))
@@ -978,6 +994,8 @@ def add_execution(
978994
fn=fn,
979995
model_names=model_names,
980996
indicator_names=ind_names,
997+
args=args,
998+
kwargs=tuple(sorted(kwargs.items())),
981999
)
9821000
)
9831001

tests/test_strategy.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,3 +2515,31 @@ def exec_fn(ctx):
25152515
strategy.add_execution(exec_fn, "SPY")
25162516
with pytest.raises(ValueError, match=re.escape("warmup must be > 0.")):
25172517
strategy.backtest(warmup=-1)
2518+
2519+
def test_backtest_when_args_and_kwargs(self, data_source_df):
2520+
def exec_fn(ctx, foo, bar=None):
2521+
assert foo == "foo_value"
2522+
assert bar == "bar_value"
2523+
2524+
strategy = Strategy(data_source_df, START_DATE, END_DATE)
2525+
strategy.add_execution(
2526+
exec_fn,
2527+
"SPY",
2528+
foo="foo_value",
2529+
bar="bar_value",
2530+
)
2531+
strategy.backtest(calc_bootstrap=False)
2532+
2533+
def test_walkforward_when_args_and_kwargs(self, data_source_df):
2534+
def exec_fn(ctx, foo, bar=None):
2535+
assert foo == "foo_value"
2536+
assert bar == "bar_value"
2537+
2538+
strategy = Strategy(data_source_df, START_DATE, END_DATE)
2539+
strategy.add_execution(
2540+
exec_fn,
2541+
"SPY",
2542+
foo="foo_value",
2543+
bar="bar_value",
2544+
)
2545+
strategy.walkforward(windows=2, calc_bootstrap=False)

0 commit comments

Comments
 (0)