Skip to content
New: Star-import, signal unraveling, Python 3.12 support, and more

# from_order_func module

Numba-compiled functions for portfolio simulation based on an order function.

## def_flex_order_func_nb function

    def_flex_order_func_nb(
        c,
        call_seq_now,
        size,
        price,
        size_type,
        direction,
        fees,
        fixed_fees,
        slippage,
        min_size,
        max_size,
        size_granularity,
        leverage,
        leverage_mode,
        reject_prob,
        price_area_vio_mode,
        allow_partial,
        raise_reject,
        log
    )

Flexible order function that creates an order based on default information.

## def_flex_pre_segment_func_nb function

    def_flex_pre_segment_func_nb(
        c,
        val_price,
        price,
        size,
        size_type,
        direction,
        auto_call_seq
    )

Flexible pre-segment function that overrides the valuation price and optionally sorts the call sequence.

## def_order_func_nb function

    def_order_func_nb(
        c,
        size,
        price,
        size_type,
        direction,
        fees,
        fixed_fees,
        slippage,
        min_size,
        max_size,
        size_granularity,
        leverage,
        leverage_mode,
        reject_prob,
        price_area_vio_mode,
        allow_partial,
        raise_reject,
        log
    )

Order function that creates an order based on default information.

## def_pre_segment_func_nb function

    def_pre_segment_func_nb(
        c,
        val_price,
        price,
        size,
        size_type,
        direction,
        auto_call_seq
    )

Pre-segment function that overrides the valuation price and optionally sorts the call sequence.

## from_flex_order_func_nb function

    from_flex_order_func_nb(
        target_shape,
        group_lens,
        cash_sharing,
        init_cash=100.0,
        init_position=0.0,
        init_price=nan,
        cash_deposits=0.0,
        cash_earnings=0.0,
        segment_mask=True,
        call_pre_segment=False,
        call_post_segment=False,
        pre_sim_func_nb=no_pre_func_nb,
        pre_sim_args=(),
        post_sim_func_nb=no_post_func_nb,
        post_sim_args=(),
        pre_group_func_nb=no_pre_func_nb,
        pre_group_args=(),
        post_group_func_nb=no_post_func_nb,
        post_group_args=(),
        pre_segment_func_nb=no_pre_func_nb,
        pre_segment_args=(),
        post_segment_func_nb=no_post_func_nb,
        post_segment_args=(),
        flex_order_func_nb=no_flex_order_func_nb,
        flex_order_args=(),
        post_order_func_nb=no_post_func_nb,
        post_order_args=(),
        index=None,
        freq=None,
        open=nan,
        high=nan,
        low=nan,
        close=nan,
        bm_close=nan,
        ffill_val_price=True,
        update_value=False,
        fill_pos_info=True,
        track_value=True,
        max_order_records=None,
        max_log_records=0,
        in_outputs=None
    )

Same as from_order_func_nb(), but with no predefined call sequence.

In contrast to `order_func_nb` infrom_order_func_nb(), `post_order_func_nb` is a segment-level order function that returns a column along with the order, and gets repeatedly called until some condition is met. This allows multiple orders to be issued within a single element and in an arbitrary order.

The order function must accept FlexOrderContext, unpacked tuple from `pre_segment_func_nb`, and `*flex_order_args`. Must return column and Order. To break out of the loop, return column of -1.

Note

Since one element can now accommodate multiple orders, you may run into "order_records index out of range" exception. In this case, you must increase `max_order_records`. This cannot be done automatically and dynamically to avoid performance degradation.

Call hierarchy

    1. pre_sim_out = pre_sim_func_nb(SimulationContext, *pre_sim_args)
        2. pre_group_out = pre_group_func_nb(GroupContext, *pre_sim_out, *pre_group_args)
            3. if call_pre_segment or segment_mask: pre_segment_out = pre_segment_func_nb(SegmentContext, *pre_group_out, *pre_segment_args)
                while col != -1:
                    4. if segment_mask: col, order = flex_order_func_nb(FlexOrderContext, *pre_segment_out, *flex_order_args)
                    5. if order: post_order_func_nb(PostOrderContext, *pre_segment_out, *post_order_args)
                    ...
            6. if call_post_segment or segment_mask: post_segment_func_nb(SegmentContext, *pre_group_out, *post_segment_args)
            ...
        7. post_group_func_nb(GroupContext, *pre_sim_out, *post_group_args)
        ...
    8. post_sim_func_nb(SimulationContext, *post_sim_args)

Let's illustrate the same example as in from_order_func_nb() but adapted for this function:

Usage

- The same example as in from_order_func_nb():

    >>> from vectorbtpro import *

    >>> @njit
    ... def pre_sim_func_nb(c):
    ...     # Create temporary arrays and pass them down the stack
    ...     print('before simulation')
    ...     order_value_out = np.empty(c.target_shape[1], dtype=np.float_)
    ...     call_seq_out = np.empty(c.target_shape[1], dtype=np.int_)
    ...     return (order_value_out, call_seq_out)

    >>> @njit
    ... def pre_group_func_nb(c, order_value_out, call_seq_out):
    ...     print('\tbefore group', c.group)
    ...     return (order_value_out, call_seq_out)

    >>> @njit
    ... def pre_segment_func_nb(c, order_value_out, call_seq_out, size, price, size_type, direction):
    ...     print('\t\tbefore segment', c.i)
    ...     for col in range(c.from_col, c.to_col):
    ...         # Here we use order price for group valuation
    ...         c.last_val_price[col] = vbt.pf_nb.select_from_col_nb(c, col, price)
    ...
    ...     # Same as for from_order_func_nb, but since we don't have a predefined c.call_seq_now anymore,
    ...     # we need to store our new call sequence somewhere else
    ...     call_seq_out[:] = np.arange(c.group_len)
    ...     vbt.pf_nb.sort_call_seq_out_nb(
    ...         c,
    ...         size,
    ...         size_type,
    ...         direction,
    ...         order_value_out[c.from_col:c.to_col],
    ...         call_seq_out[c.from_col:c.to_col]
    ...     )
    ...
    ...     # Forward the sorted call sequence
    ...     return (call_seq_out,)

    >>> @njit
    ... def flex_order_func_nb(c, call_seq_out, size, price, size_type, direction, fees, fixed_fees, slippage):
    ...     if c.call_idx < c.group_len:
    ...         col = c.from_col + call_seq_out[c.call_idx]
    ...         print('\t\t\tcreating order', c.call_idx, 'at column', col)
    ...         # # Create and return an order
    ...         return col, vbt.pf_nb.order_nb(
    ...             size=vbt.pf_nb.select_from_col_nb(c, col, size),
    ...             price=vbt.pf_nb.select_from_col_nb(c, col, price),
    ...             size_type=vbt.pf_nb.select_from_col_nb(c, col, size_type),
    ...             direction=vbt.pf_nb.select_from_col_nb(c, col, direction),
    ...             fees=vbt.pf_nb.select_from_col_nb(c, col, fees),
    ...             fixed_fees=vbt.pf_nb.select_from_col_nb(c, col, fixed_fees),
    ...             slippage=vbt.pf_nb.select_from_col_nb(c, col, slippage)
    ...         )
    ...     # All columns already processed -> break the loop
    ...     print('\t\t\tbreaking out of the loop')
    ...     return -1, vbt.pf_nb.order_nothing_nb()

    >>> @njit
    ... def post_order_func_nb(c, call_seq_out):
    ...     print('\t\t\t\torder status:', c.order_result.status)
    ...     return None

    >>> @njit
    ... def post_segment_func_nb(c, order_value_out, call_seq_out):
    ...     print('\t\tafter segment', c.i)
    ...     return None

    >>> @njit
    ... def post_group_func_nb(c, order_value_out, call_seq_out):
    ...     print('\tafter group', c.group)
    ...     return None

    >>> @njit
    ... def post_sim_func_nb(c):
    ...     print('after simulation')
    ...     return None

    >>> target_shape = (5, 3)
    >>> np.random.seed(42)
    >>> group_lens = np.array([3])  # one group of three columns
    >>> cash_sharing = True
    >>> segment_mask = np.array([True, False, True, False, True])[:, None]
    >>> price = close = np.random.uniform(1, 10, size=target_shape)
    >>> size = np.array([[1 / target_shape[1]]])  # custom flexible arrays must be 2-dim
    >>> size_type = np.array([[vbt.pf_enums.SizeType.TargetPercent]])
    >>> direction = np.array([[vbt.pf_enums.Direction.LongOnly]])
    >>> fees = np.array([[0.001]])
    >>> fixed_fees = np.array([[1.]])
    >>> slippage = np.array([[0.001]])

    >>> sim_out = vbt.pf_nb.from_flex_order_func_nb(
    ...     target_shape,
    ...     group_lens,
    ...     cash_sharing,
    ...     segment_mask=segment_mask,
    ...     pre_sim_func_nb=pre_sim_func_nb,
    ...     post_sim_func_nb=post_sim_func_nb,
    ...     pre_group_func_nb=pre_group_func_nb,
    ...     post_group_func_nb=post_group_func_nb,
    ...     pre_segment_func_nb=pre_segment_func_nb,
    ...     pre_segment_args=(size, price, size_type, direction),
    ...     post_segment_func_nb=post_segment_func_nb,
    ...     flex_order_func_nb=flex_order_func_nb,
    ...     flex_order_args=(size, price, size_type, direction, fees, fixed_fees, slippage),
    ...     post_order_func_nb=post_order_func_nb
    ... )
    before simulation
        before group 0
            before segment 0
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 1
                    order status: 0
                creating order 2 at column 2
                    order status: 0
                breaking out of the loop
            after segment 0
            before segment 2
                creating order 0 at column 1
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 0
                    order status: 0
                breaking out of the loop
            after segment 2
            before segment 4
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 1
                    order status: 0
                breaking out of the loop
            after segment 4
        after group 0
    after simulation

## from_flex_order_func_rw_nb function

    from_flex_order_func_rw_nb(
        target_shape,
        group_lens,
        cash_sharing,
        init_cash=100.0,
        init_position=0.0,
        init_price=nan,
        cash_deposits=0.0,
        cash_earnings=0.0,
        segment_mask=True,
        call_pre_segment=False,
        call_post_segment=False,
        pre_sim_func_nb=no_pre_func_nb,
        pre_sim_args=(),
        post_sim_func_nb=no_post_func_nb,
        post_sim_args=(),
        pre_row_func_nb=no_pre_func_nb,
        pre_row_args=(),
        post_row_func_nb=no_post_func_nb,
        post_row_args=(),
        pre_segment_func_nb=no_pre_func_nb,
        pre_segment_args=(),
        post_segment_func_nb=no_post_func_nb,
        post_segment_args=(),
        flex_order_func_nb=no_flex_order_func_nb,
        flex_order_args=(),
        post_order_func_nb=no_post_func_nb,
        post_order_args=(),
        index=None,
        freq=None,
        open=nan,
        high=nan,
        low=nan,
        close=nan,
        bm_close=nan,
        ffill_val_price=True,
        update_value=False,
        fill_pos_info=True,
        track_value=True,
        max_order_records=None,
        max_log_records=0,
        in_outputs=None
    )

Same as from_flex_order_func_nb(), but iterates using row-major order, with the rows changing fastest, and the columns/groups changing slowest.

Call hierarchy

    1. pre_sim_out = pre_sim_func_nb(SimulationContext, *pre_sim_args)
        2. pre_row_out = pre_row_func_nb(RowContext, *pre_sim_out, *pre_row_args)
            3. if call_pre_segment or segment_mask: pre_segment_out = pre_segment_func_nb(SegmentContext, *pre_row_out, *pre_segment_args)
                while col != -1:
                    4. if segment_mask: col, order = flex_order_func_nb(FlexOrderContext, *pre_segment_out, *flex_order_args)
                    5. if order: post_order_func_nb(PostOrderContext, *pre_segment_out, *post_order_args)
                    ...
            6. if call_post_segment or segment_mask: post_segment_func_nb(SegmentContext, *pre_row_out, *post_segment_args)
            ...
        7. post_row_func_nb(RowContext, *pre_sim_out, *post_row_args)
        ...
    8. post_sim_func_nb(SimulationContext, *post_sim_args)

Let's illustrate the same example as in from_order_func_nb() but adapted for this function:

    >>> @njit
    ... def pre_row_func_nb(c, order_value_out, call_seq_out):
    ...     print('\tbefore row', c.i)
    ...     return (order_value_out, call_seq_out)

    >>> @njit
    ... def post_row_func_nb(c, order_value_out, call_seq_out):
    ...     print('\tafter row', c.i)
    ...     return None

    >>> sim_out = vbt.pf_nb.from_flex_order_func_rw_nb(
    ...     target_shape,
    ...     group_lens,
    ...     cash_sharing,
    ...     segment_mask=segment_mask,
    ...     pre_sim_func_nb=pre_sim_func_nb,
    ...     post_sim_func_nb=post_sim_func_nb,
    ...     pre_row_func_nb=pre_row_func_nb,
    ...     post_row_func_nb=post_row_func_nb,
    ...     pre_segment_func_nb=pre_segment_func_nb,
    ...     pre_segment_args=(size, price, size_type, direction),
    ...     post_segment_func_nb=post_segment_func_nb,
    ...     flex_order_func_nb=flex_order_func_nb,
    ...     flex_order_args=(size, price, size_type, direction, fees, fixed_fees, slippage),
    ...     post_order_func_nb=post_order_func_nb
    ... )

## from_order_func_nb function

    from_order_func_nb(
        target_shape,
        group_lens,
        cash_sharing,
        call_seq=None,
        init_cash=100.0,
        init_position=0.0,
        init_price=nan,
        cash_deposits=0.0,
        cash_earnings=0.0,
        segment_mask=True,
        call_pre_segment=False,
        call_post_segment=False,
        pre_sim_func_nb=no_pre_func_nb,
        pre_sim_args=(),
        post_sim_func_nb=no_post_func_nb,
        post_sim_args=(),
        pre_group_func_nb=no_pre_func_nb,
        pre_group_args=(),
        post_group_func_nb=no_post_func_nb,
        post_group_args=(),
        pre_segment_func_nb=no_pre_func_nb,
        pre_segment_args=(),
        post_segment_func_nb=no_post_func_nb,
        post_segment_args=(),
        order_func_nb=no_order_func_nb,
        order_args=(),
        post_order_func_nb=no_post_func_nb,
        post_order_args=(),
        index=None,
        freq=None,
        open=nan,
        high=nan,
        low=nan,
        close=nan,
        bm_close=nan,
        ffill_val_price=True,
        update_value=False,
        fill_pos_info=True,
        track_value=True,
        max_order_records=None,
        max_log_records=0,
        in_outputs=None
    )

Fill order and log records by iterating over a shape and calling a range of user-defined functions.

Starting with initial cash `init_cash`, iterates over each group and column in `target_shape`, and for each data point, generates an order using `order_func_nb`. Tries then to fulfill that order. Upon success, updates the current state including the cash balance and the position. Returns SimulationOutput.

As opposed to from_order_func_rw_nb(), order processing happens in column-major order. Column-major order means processing the entire column/group with all rows before moving to the next one. See Row- and column-major order.

Args

`target_shape` :`tuple`
  See SimulationContext.target_shape.
`group_lens` :`array_like` of `int`
  See SimulationContext.group_lens.
`cash_sharing` :`bool`
  See SimulationContext.cash_sharing.
`call_seq` :`array_like` of `int`
  See SimulationContext.call_seq.
`init_cash` :`array_like` of `float`
  See SimulationContext.init_cash.
`init_position` :`array_like` of `float`
  See SimulationContext.init_position.
`init_price` :`array_like` of `float`
  See SimulationContext.init_price.
`cash_deposits` :`array_like` of `float`
  See SimulationContext.cash_deposits.
`cash_earnings` :`array_like` of `float`
  See SimulationContext.cash_earnings.
`segment_mask` :`array_like` of `bool`
  See SimulationContext.segment_mask.
`call_pre_segment` :`bool`
  See SimulationContext.call_pre_segment.
`call_post_segment` :`bool`
  See SimulationContext.call_post_segment.
`pre_sim_func_nb` :`callable`

  Function called before simulation.

  Can be used for creation of global arrays and setting the seed.

  Must accept SimulationContext and `*pre_sim_args`. Must return a tuple of any content, which is then passed to `pre_group_func_nb` and `post_group_func_nb`.

`pre_sim_args` :`tuple`
  Packed arguments passed to `pre_sim_func_nb`.
`post_sim_func_nb` :`callable`

  Function called after simulation.

  Must accept SimulationContext and `*post_sim_args`. Must return nothing.

`post_sim_args` :`tuple`
  Packed arguments passed to `post_sim_func_nb`.
`pre_group_func_nb` :`callable`

  Function called before each group.

  Must accept GroupContext, unpacked tuple from `pre_sim_func_nb`, and `*pre_group_args`. Must return a tuple of any content, which is then passed to `pre_segment_func_nb` and `post_segment_func_nb`.

`pre_group_args` :`tuple`
  Packed arguments passed to `pre_group_func_nb`.
`post_group_func_nb` :`callable`

  Function called after each group.

  Must accept GroupContext, unpacked tuple from `pre_sim_func_nb`, and `*post_group_args`. Must return nothing.

`post_group_args` :`tuple`
  Packed arguments passed to `post_group_func_nb`.
`pre_segment_func_nb` :`callable`

  Function called before each segment.

  Called if `segment_mask` or `call_pre_segment` is True.

  Must accept SegmentContext, unpacked tuple from `pre_group_func_nb`, and `*pre_segment_args`. Must return a tuple of any content, which is then passed to `order_func_nb` and `post_order_func_nb`.

  This is the right place to change call sequence and set the valuation price. Group re-valuation and update of the open position stats happens right after this function, regardless of whether it has been called.

  Note

  To change the call sequence of a segment, access SegmentContext.call_seq_now and change it in-place. Make sure to not generate any new arrays as it may negatively impact performance. Assigning SegmentContext.call_seq_now as any other context (named tuple) value is not supported. See SegmentContext.call_seq_now.

  Note

  You can override elements of `last_val_price` to manipulate group valuation. See SimulationContext.last_val_price.

`pre_segment_args` :`tuple`
  Packed arguments passed to `pre_segment_func_nb`.
`post_segment_func_nb` :`callable`

  Function called after each segment.

  Called if `segment_mask` or `call_post_segment` is True.

  Addition of cash_earnings, the final group re-valuation, and the final update of the open position stats happens right before this function, regardless of whether it has been called.

  The passed context represents the final state of each segment, thus makes sure to do any changes before this function is called.

  Must accept SegmentContext, unpacked tuple from `pre_group_func_nb`, and `*post_segment_args`. Must return nothing.

`post_segment_args` :`tuple`
  Packed arguments passed to `post_segment_func_nb`.
`order_func_nb` :`callable`

  Order generation function.

  Used for either generating an order or skipping.

  Must accept OrderContext, unpacked tuple from `pre_segment_func_nb`, and `*order_args`. Must return Order.

  Note

  If the returned order has been rejected, there is no way of issuing a new order. You should make sure that the order passes, for example, by using try_order_nb().

  To have a greater freedom in order management, use from_flex_order_func_nb().

`order_args` :`tuple`
  Arguments passed to `order_func_nb`.
`post_order_func_nb` :`callable`

  Callback that is called after the order has been processed.

  Used for checking the order status and doing some post-processing.

  Must accept PostOrderContext, unpacked tuple from `pre_segment_func_nb`, and `*post_order_args`. Must return nothing.

`post_order_args` :`tuple`
  Arguments passed to `post_order_func_nb`.
`index` :`array`
  See SimulationContext.index.
`freq` :`int`
  See SimulationContext.freq.
`open` :`array_like` of `float`
  See SimulationContext.open.
`high` :`array_like` of `float`
  See SimulationContext.high.
`low` :`array_like` of `float`
  See SimulationContext.low.
`close` :`array_like` of `float`
  See SimulationContext.close.
`bm_close` :`array_like` of `float`
  See SimulationContext.bm_close.
`ffill_val_price` :`bool`
  See SimulationContext.ffill_val_price.
`update_value` :`bool`
  See SimulationContext.update_value.
`fill_pos_info` :`bool`
  See SimulationContext.fill_pos_info.
`track_value` :`bool`
  See SimulationContext.track_value.
`max_order_records` :`int`
  The max number of order records expected to be filled at each column.
`max_log_records` :`int`
  The max number of log records expected to be filled at each column.
`in_outputs` :`bool`
  See SimulationContext.in_outputs.

Note

Remember that indexing of 2-dim arrays in vectorbt follows that of pandas: `a[i, col]`.

Warning

You can only safely access data of columns that are to the left of the current group and rows that are to the top of the current row within the same group. Other data points have not been processed yet and thus empty. Accessing them will not trigger any errors or warnings, but provide you with arbitrary data (see np.empty).

Call hierarchy

Like most things in the vectorbt universe, simulation is also done by iterating over a (imaginary) frame. This frame consists of two dimensions: time (rows) and assets/features (columns). Each element of this frame is a potential order, which gets generated by calling an order function.

The question is: how do we move across this frame to simulate trading? There are two movement patterns: column-major (as done by from_order_func_nb()) and row-major order (as done by from_order_func_rw_nb()). In each of these patterns, we are always moving from top to bottom (time axis) and from left to right (asset/feature axis); the only difference between them is across which axis we are moving faster: do we want to process each column first (thus assuming that columns are independent) or each row? Choosing between them is mostly a matter of preference, but it also makes different data being available when generating an order.

The frame is further divided into "blocks": columns, groups, rows, segments, and elements. For example, columns can be grouped into groups that may or may not share the same capital. Regardless of capital sharing, each collection of elements within a group and a time step is called a segment, which simply defines a single context (such as shared capital) for one or multiple orders. Each segment can also define a custom sequence (a so-called call sequence) in which orders are executed.

You can imagine each of these blocks as a rectangle drawn over different parts of the frame, and having its own context and pre/post-processing function. The pre-processing function is a simple callback that is called before entering the block, and can be provided by the user to, for example, prepare arrays or do some custom calculations. It must return a tuple (can be empty) that is then unpacked and passed as arguments to the pre- and postprocessing function coming next in the call hierarchy. The postprocessing function can be used, for example, to write user-defined arrays such as returns.

    1. pre_sim_out = pre_sim_func_nb(SimulationContext, *pre_sim_args)
        2. pre_group_out = pre_group_func_nb(GroupContext, *pre_sim_out, *pre_group_args)
            3. if call_pre_segment or segment_mask: pre_segment_out = pre_segment_func_nb(SegmentContext, *pre_group_out, *pre_segment_args)
                4. if segment_mask: order = order_func_nb(OrderContext, *pre_segment_out, *order_args)
                5. if order: post_order_func_nb(PostOrderContext, *pre_segment_out, *post_order_args)
                ...
            6. if call_post_segment or segment_mask: post_segment_func_nb(SegmentContext, *pre_group_out, *post_segment_args)
            ...
        7. post_group_func_nb(GroupContext, *pre_sim_out, *post_group_args)
        ...
    8. post_sim_func_nb(SimulationContext, *post_sim_args)

Let's demonstrate a frame with one group of two columns and one group of one column, and the following call sequence:

    array([[0, 1, 0],
           [1, 0, 0]])

And here is the context information available at each step:

Usage

- Create a group of three assets together sharing 100$ and simulate an equal-weighted portfolio that rebalances every second tick, all without leaving Numba:

    >>> from vectorbtpro import *

    >>> @njit
    ... def pre_sim_func_nb(c):
    ...     print('before simulation')
    ...     # Create a temporary array and pass it down the stack
    ...     order_value_out = np.empty(c.target_shape[1], dtype=np.float_)
    ...     return (order_value_out,)

    >>> @njit
    ... def pre_group_func_nb(c, order_value_out):
    ...     print('\tbefore group', c.group)
    ...     # Forward down the stack (you can omit pre_group_func_nb entirely)
    ...     return (order_value_out,)

    >>> @njit
    ... def pre_segment_func_nb(c, order_value_out, size, price, size_type, direction):
    ...     print('\t\tbefore segment', c.i)
    ...     for col in range(c.from_col, c.to_col):
    ...         # Here we use order price for group valuation
    ...         c.last_val_price[col] = vbt.pf_nb.select_from_col_nb(c, col, price)
    ...
    ...     # Reorder call sequence of this segment such that selling orders come first and buying last
    ...     # Rearranges c.call_seq_now based on order value (size, size_type, direction, and val_price)
    ...     # Utilizes flexible indexing using select_from_col_nb (as we did above)
    ...     vbt.pf_nb.sort_call_seq_nb(
    ...         c,
    ...         size,
    ...         size_type,
    ...         direction,
    ...         order_value_out[c.from_col:c.to_col]
    ...     )
    ...     # Forward nothing
    ...     return ()

    >>> @njit
    ... def order_func_nb(c, size, price, size_type, direction, fees, fixed_fees, slippage):
    ...     print('\t\t\tcreating order', c.call_idx, 'at column', c.col)
    ...     # Create and return an order
    ...     return vbt.pf_nb.order_nb(
    ...         size=vbt.pf_nb.select_nb(c, size),
    ...         price=vbt.pf_nb.select_nb(c, price),
    ...         size_type=vbt.pf_nb.select_nb(c, size_type),
    ...         direction=vbt.pf_nb.select_nb(c, direction),
    ...         fees=vbt.pf_nb.select_nb(c, fees),
    ...         fixed_fees=vbt.pf_nb.select_nb(c, fixed_fees),
    ...         slippage=vbt.pf_nb.select_nb(c, slippage)
    ...     )

    >>> @njit
    ... def post_order_func_nb(c):
    ...     print('\t\t\t\torder status:', c.order_result.status)
    ...     return None

    >>> @njit
    ... def post_segment_func_nb(c, order_value_out):
    ...     print('\t\tafter segment', c.i)
    ...     return None

    >>> @njit
    ... def post_group_func_nb(c, order_value_out):
    ...     print('\tafter group', c.group)
    ...     return None

    >>> @njit
    ... def post_sim_func_nb(c):
    ...     print('after simulation')
    ...     return None

    >>> target_shape = (5, 3)
    >>> np.random.seed(42)
    >>> group_lens = np.array([3])  # one group of three columns
    >>> cash_sharing = True
    >>> segment_mask = np.array([True, False, True, False, True])[:, None]
    >>> price = close = np.random.uniform(1, 10, size=target_shape)
    >>> size = np.array([[1 / target_shape[1]]])  # custom flexible arrays must be 2-dim
    >>> size_type = np.array([[vbt.pf_enums.SizeType.TargetPercent]])
    >>> direction = np.array([[vbt.pf_enums.Direction.LongOnly]])
    >>> fees = np.array([[0.001]])
    >>> fixed_fees = np.array([[1.]])
    >>> slippage = np.array([[0.001]])

    >>> sim_out = vbt.pf_nb.from_order_func_nb(
    ...     target_shape,
    ...     group_lens,
    ...     cash_sharing,
    ...     segment_mask=segment_mask,
    ...     pre_sim_func_nb=pre_sim_func_nb,
    ...     post_sim_func_nb=post_sim_func_nb,
    ...     pre_group_func_nb=pre_group_func_nb,
    ...     post_group_func_nb=post_group_func_nb,
    ...     pre_segment_func_nb=pre_segment_func_nb,
    ...     pre_segment_args=(size, price, size_type, direction),
    ...     post_segment_func_nb=post_segment_func_nb,
    ...     order_func_nb=order_func_nb,
    ...     order_args=(size, price, size_type, direction, fees, fixed_fees, slippage),
    ...     post_order_func_nb=post_order_func_nb
    ... )
    before simulation
        before group 0
            before segment 0
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 1
                    order status: 0
                creating order 2 at column 2
                    order status: 0
            after segment 0
            before segment 2
                creating order 0 at column 1
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 0
                    order status: 0
            after segment 2
            before segment 4
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 1
                    order status: 0
            after segment 4
        after group 0
    after simulation

    >>> pd.DataFrame.from_records(sim_out.order_records)
       id  col  idx       size     price      fees  side
    0   0    0    0   7.626262  4.375232  1.033367     0
    1   1    0    2   5.210115  1.524275  1.007942     0
    2   2    0    4   7.899568  8.483492  1.067016     1
    3   0    1    0   3.488053  9.565985  1.033367     0
    4   1    1    2   0.920352  8.786790  1.008087     1
    5   2    1    4  10.713236  2.913963  1.031218     0
    6   0    2    0   3.972040  7.595533  1.030170     0
    7   1    2    2   0.448747  6.403625  1.002874     1
    8   2    2    4  12.378281  2.639061  1.032667     0

    >>> col_map = vbt.rec_nb.col_map_nb(sim_out.order_records['col'], target_shape[1])
    >>> asset_flow = vbt.pf_nb.asset_flow_nb(target_shape, sim_out.order_records, col_map)
    >>> assets = vbt.pf_nb.assets_nb(asset_flow)
    >>> asset_value = vbt.pf_nb.asset_value_nb(close, assets)
    >>> vbt.Scatter(data=asset_value).fig.show()

Note that the last order in a group with cash sharing is always disadvantaged as it has a bit less funds than the previous orders due to costs, which are not included when valuating the group.

## from_order_func_rw_nb function

    from_order_func_rw_nb(
        target_shape,
        group_lens,
        cash_sharing,
        call_seq=None,
        init_cash=100.0,
        init_position=0.0,
        init_price=nan,
        cash_deposits=0.0,
        cash_earnings=0.0,
        segment_mask=True,
        call_pre_segment=False,
        call_post_segment=False,
        pre_sim_func_nb=no_pre_func_nb,
        pre_sim_args=(),
        post_sim_func_nb=no_post_func_nb,
        post_sim_args=(),
        pre_row_func_nb=no_pre_func_nb,
        pre_row_args=(),
        post_row_func_nb=no_post_func_nb,
        post_row_args=(),
        pre_segment_func_nb=no_pre_func_nb,
        pre_segment_args=(),
        post_segment_func_nb=no_post_func_nb,
        post_segment_args=(),
        order_func_nb=no_order_func_nb,
        order_args=(),
        post_order_func_nb=no_post_func_nb,
        post_order_args=(),
        index=None,
        freq=None,
        open=nan,
        high=nan,
        low=nan,
        close=nan,
        bm_close=nan,
        ffill_val_price=True,
        update_value=False,
        fill_pos_info=True,
        track_value=True,
        max_order_records=None,
        max_log_records=0,
        in_outputs=None
    )

Same as from_order_func_nb(), but iterates in row-major order.

Row-major order means processing the entire row with all groups/columns before moving to the next one.

The main difference is that instead of `pre_group_func_nb` it now exposes `pre_row_func_nb`, which is executed per entire row. It must accept RowContext.

Note

Function `pre_row_func_nb` is only called if there is at least on active segment in the row. Functions `pre_segment_func_nb` and `order_func_nb` are only called if their segment is active. If the main task of `pre_row_func_nb` is to activate/deactivate segments, all segments must be activated by default to allow `pre_row_func_nb` to be called.

Warning

You can only safely access data points that are to the left of the current group and rows that are to the top of the current row.

Call hierarchy

    1. pre_sim_out = pre_sim_func_nb(SimulationContext, *pre_sim_args)
        2. pre_row_out = pre_row_func_nb(RowContext, *pre_sim_out, *pre_row_args)
            3. if call_pre_segment or segment_mask: pre_segment_out = pre_segment_func_nb(SegmentContext, *pre_row_out, *pre_segment_args)
                4. if segment_mask: order = order_func_nb(OrderContext, *pre_segment_out, *order_args)
                5. if order: post_order_func_nb(PostOrderContext, *pre_segment_out, *post_order_args)
                ...
            6. if call_post_segment or segment_mask: post_segment_func_nb(SegmentContext, *pre_row_out, *post_segment_args)
            ...
        7. post_row_func_nb(RowContext, *pre_sim_out, *post_row_args)
        ...
    8. post_sim_func_nb(SimulationContext, *post_sim_args)

Let's illustrate the same example as in from_order_func_nb() but adapted for this function:

Usage

- Running the same example as in from_order_func_nb() but adapted for this function:

    >>> @njit
    ... def pre_row_func_nb(c, order_value_out):
    ...     print('\tbefore row', c.i)
    ...     # Forward down the stack
    ...     return (order_value_out,)

    >>> @njit
    ... def post_row_func_nb(c, order_value_out):
    ...     print('\tafter row', c.i)
    ...     return None

    >>> sim_out = vbt.pf_nb.from_order_func_rw_nb(
    ...     target_shape,
    ...     group_lens,
    ...     cash_sharing,
    ...     segment_mask=segment_mask,
    ...     pre_sim_func_nb=pre_sim_func_nb,
    ...     post_sim_func_nb=post_sim_func_nb,
    ...     pre_row_func_nb=pre_row_func_nb,
    ...     post_row_func_nb=post_row_func_nb,
    ...     pre_segment_func_nb=pre_segment_func_nb,
    ...     pre_segment_args=(size, price, size_type, direction),
    ...     post_segment_func_nb=post_segment_func_nb,
    ...     order_func_nb=order_func_nb,
    ...     order_args=(size, price, size_type, direction, fees, fixed_fees, slippage),
    ...     post_order_func_nb=post_order_func_nb
    ... )
    before simulation
        before row 0
            before segment 0
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 1
                    order status: 0
                creating order 2 at column 2
                    order status: 0
            after segment 0
        after row 0
        before row 1
        after row 1
        before row 2
            before segment 2
                creating order 0 at column 1
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 0
                    order status: 0
            after segment 2
        after row 2
        before row 3
        after row 3
        before row 4
            before segment 4
                creating order 0 at column 0
                    order status: 0
                creating order 1 at column 2
                    order status: 0
                creating order 2 at column 1
                    order status: 0
            after segment 4
        after row 4
    after simulation

## get_ctx_group_value_nb function

    get_ctx_group_value_nb(
        seg_ctx
    )

Get group value from context.

Accepts SegmentContext.

Best called once from `pre_segment_func_nb`. To set the valuation price, change `last_val_price` of the context in-place.

Note

Cash sharing must be enabled.

## no_flex_order_func_nb function

    no_flex_order_func_nb(
        c,
        *args
    )

Placeholder flexible order function that returns "break" column and no order.

## no_order_func_nb function

    no_order_func_nb(
        c,
        *args
    )

Placeholder order function that returns no order.

## no_post_func_nb function

    no_post_func_nb(
        c,
        *args
    )

Placeholder postprocessing function that returns nothing.

## no_pre_func_nb function

    no_pre_func_nb(
        c,
        *args
    )

Placeholder preprocessing function that forwards received arguments down the stack.

## set_val_price_nb function

    set_val_price_nb(
        c,
        val_price,
        price
    )

Override valuation price in a context.

Allows specifying a valuation price of positive infinity (takes the current price) and negative infinity (takes the latest valuation price).

## sort_call_seq_1d_nb function

    sort_call_seq_1d_nb(
        ctx,
        size,
        size_type,
        direction,
        order_value_out
    )

Sort call sequence attached to SegmentContext.

See sort_call_seq_out_1d_nb().

Note

Can only be used in non-flexible simulation functions.

## sort_call_seq_nb function

    sort_call_seq_nb(
        ctx,
        size,
        size_type,
        direction,
        order_value_out
    )

Sort call sequence attached to SegmentContext.

See sort_call_seq_out_nb().

Note

Can only be used in non-flexible simulation functions.

## sort_call_seq_out_1d_nb function

    sort_call_seq_out_1d_nb(
        ctx,
        size,
        size_type,
        direction,
        order_value_out,
        call_seq_out
    )

Sort call sequence `call_seq_out` based on the value of each potential order.

Accepts SegmentContext and other arguments, sorts `call_seq_out` in place, and returns nothing.

Arrays `size`, `size_type`, and `direction` utilize flexible indexing; they must be 1-dim arrays that broadcast to `group_len`.

The lengths of `order_value_out` and `call_seq_out` must match the number of columns in the group. Array `order_value_out` must be empty and will contain sorted order values after execution. Array `call_seq_out` must be filled with integers ranging from 0 to the number of columns in the group (in this exact order).

Best called once from `pre_segment_func_nb`.

Note

Cash sharing must be enabled and `call_seq_out` must follow `CallSeqType.Default`.

## sort_call_seq_out_nb function

    sort_call_seq_out_nb(
        ctx,
        size,
        size_type,
        direction,
        order_value_out,
        call_seq_out
    )

Same as sort_call_seq_out_1d_nb() but with `size`, `size_type`, and `direction` being 2-dim arrays.

## try_order_nb function

    try_order_nb(
        ctx,
        order
    )

Execute an order without persistence.

#### Cookie consent

We use cookies to recognize your repeated visits and preferences, as well as to measure the effectiveness of our documentation and whether users find what they're searching for. With your consent, you're helping us to make our documentation better.

- Google Analytics
- GitHub

Manage settings