Skip to content

from_order_func module

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


calc_ctx_group_value_nb function

calc_ctx_group_value_nb(
    seg_ctx
)

Calculate 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.


calc_group_value_nb function

calc_group_value_nb(
    from_col,
    to_col,
    cash_now,
    last_position,
    last_val_price
)

Calculate group value.


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,
    sim_start=None,
    sim_end=None,
    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

>>> 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=float_)
...     call_seq_out = np.empty(c.target_shape[1], dtype=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,
    sim_start=None,
    sim_end=None,
    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,
    sim_start=None,
    sim_end=None,
    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=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,
    sim_start=None,
    sim_end=None,
    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

>>> @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

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.