pybroker icon indicating copy to clipboard operation
pybroker copied to clipboard

adding *args, **kwargs to add_execution, set_after_exec and set_before_exec

Open none2003 opened this issue 4 months ago • 1 comments

Currently, if you want to pass parameters to fn, you must use the pybroker.param method, which is rather inconvenient, eg:

pybroker.param("run_type", "monthly")

def some_exec(ctx):
    run_type = pybroker.param("run_type")
    if run_type:
        if run_type == "monthly":
            pass

So, it will be great if *args, **kwargs can be add to add_execution, set_after_exec and set_before_exec, like:

def add_execution(
    self,
    fn: Optional[Callable[[ExecContext], None]],
    symbols: Union[str, Iterable[str]],
    models: Optional[Union[ModelSource, Iterable[ModelSource]]] = None,
    indicators: Optional[Union[Indicator, Iterable[Indicator]]] = None,
    *args, 
    **kwargs,
):

these extra args can later be passed to fn directly and then referenced within fn, thereby increasing the flexibility of fn.

For example:

strategy.add_execution(some_exec, symbols, indicators=[inv_vol], run_type="monthly")

def some_exec(ctx, run_type):
    if run_type:
        if run_type == "monthly":
            pass

set_after_exec and set_before_exec can also be handled in the same way.

none2003 avatar Aug 19 '25 01:08 none2003

Thanks for the suggestion @none2003, I will add this for the next release.

edtechre avatar Aug 27 '25 01:08 edtechre

Added in c024c4e

edtechre avatar Dec 05 '25 08:12 edtechre

Hi @edtechre,

I hope you're doing well! It's wonderful to see that version 1.2.11 has been released, and I truly appreciate the effort you’ve put into including the feature mentioned in this issue. Thank you for your hard work and dedication.

I’ve had the chance to test it, and I noticed that the method in the first parameter of add_execution correctly receives the additional *args and **kwargs parameters. However, it seems that the set_after_exec and set_before_exec methods do not. Upon reviewing the commit history, it appears that the functionality of these 2 methods to receive *args and **kwargs parameters and pass them to the method specified by the first argument has not been included in this release. Could you kindly clarify if this was intentional or perhaps an oversight? Your guidance would be greatly appreciated.

Thank you again for your time and efforts!

none2003 avatar Dec 06 '25 05:12 none2003

Thanks @none2003. I looked at this again, but I think adding *args and **kwargs to the set_before_exec and set_after_exec signatures make them convoluted since a Mapping of [str, ExecContext] is passed to the function. I would recommend using functools.partial here instead.

edtechre avatar Dec 23 '25 09:12 edtechre

Hi @edtechre,

Thanks @none2003. I looked at this again, but I think adding *args and **kwargs to the set_before_exec and set_after_exec signatures make them convoluted since a Mapping of [str, ExecContext] is passed to the function. I would recommend using functools.partial here instead.

Could you kindly provide an example of using functools.partial in the context of set_after_exec? If possible, I would greatly appreciate it if you could reference the example from the pybroker documentation.

def optimization(ctxs: dict[str, ExecContext]):
    lookback = pyb.param('lookback')
    if start_of_month(ctxs):
        Y = calculate_returns(ctxs, lookback)
        port = rp.Portfolio(returns=Y)
        port.assets_stats(method_mu='hist', method_cov='hist')
        w = port.optimization(
            model='Classic',
            rm='CVaR',
            obj='MinRisk',
            rf=0,      # Risk free rate.
            l=0,       # Risk aversion factor.
            hist=True  # Use historical scenarios.
        )
        targets = {
            symbol: w.T[symbol].values[0]
            for symbol in ctxs.keys()
        }
        set_target_shares(ctxs, targets)
strategy.set_after_exec(optimization)
result = strategy.backtest(warmup=pyb.param('lookback'))

Originally, the lookback parameter needs to be passed through pyb.param. If we were to use functools.partial as you kindly suggested, may I ask how this value should be passed?

none2003 avatar Dec 23 '25 11:12 none2003

You can bind a lookback variable to your optimization function.

def optimization(ctxs: dict[str, ExecContext], lookback: int):
     ...

fn = functools.partial(optimization, lookback=...)
strategy.set_after_exec(fn)

edtechre avatar Dec 23 '25 23:12 edtechre

You can bind a lookback variable to your optimization function.

def optimization(ctxs: dict[str, ExecContext], lookback: int):
     ...

fn = functools.partial(optimization, lookback=...)
strategy.set_after_exec(fn)

This is fantastic! I gave it a try, and it works wonderfully. Thank you so much for your effort. It seems that the changes in your c024c4 commit could also be addressed in this way, which means there's no longer a need to modify the source code.

none2003 avatar Dec 24 '25 08:12 none2003