yabte.backtest

Strategy

class yabte.backtest.strategy.Strategy(*, orders: Orders | None = None, params: Series | None = None, books: Dict[str, Book] | None = None, assets: Dict[str, Asset] | None = None, _ts: Timestamp | None = None, _data_lock: bool = True, _mask_open: bool = False)[source]

Bases: object

Trading strategy base class.

This class should be derived from and will be instantiated by StrategyRunner.

orders: Orders | None = None

Double ended queue of orders.

params: Series | None = None

Parameters supplied to strategy.

books: Dict[str, Book] | None = None

Dictionary of books.

assets: Dict[str, Asset] | None = None

Dictionary of assets.

property ts: Timestamp | None

Stores the current timestamp.

property data: DataFrame

Provides window of data available up to current timestamp self.ts and masks out data not availble at open (like high, low, close, volume).

init()[source]

Initialise internal variables & enhance data for strategy.

on_open()[source]

Executed on open every day.

Use self.ts to determine current timestamp. Data available at this timestamp is accessible from self.data.

on_close()[source]

Executed on close every day.

Use self.ts to determine current timestamp. Data available at this timestamp is accessible from self.data.

StrategyRunner

class yabte.backtest.strategyrunner.StrategyRunnerResult(*, books: List[yabte.backtest.book.Book], strategies: List[yabte.backtest.strategy.Strategy], assets: List[yabte.backtest.asset.Asset], _orders_unprocessed: yabte.backtest.order.Orders = <factory>, _orders_processed: List[yabte.backtest.order.Order] = <factory>, _book_history: Optional[pandas.core.frame.DataFrame] = None)[source]

Bases: object

books: List[Book]

Books used within this result.

strategies: List[Strategy]

Strategies used within this result.

assets: List[Asset]

Assets used within this result.

property orders_unprocessed: Orders

Unprocessed orders queue.

property orders_processed: List[Order]

Processed orders list.

property book_history: DataFrame

Dataframe with book cash, mtm and total value history.

property transaction_history: DataFrame

Dataframe with trade history.

class yabte.backtest.strategyrunner.StrategyRunner(*, data: ~pandas.core.frame.DataFrame, assets: ~typing.List[~yabte.backtest.asset.Asset], strategies: ~typing.List[~yabte.backtest.strategy.Strategy], mandates: ~typing.Dict[str, ~yabte.backtest.book.BookMandate] = <factory>, books: ~typing.List[~yabte.backtest.book.Book] = <factory>)[source]

Bases: object

Encapsulates the execution of multiple strategies.

Orders are captured in orders_processed and orders_unprocessed. books is a list of books and if none provided a single book is created called ‘Main’. After execution summary book and trade histories are captured in book_history and transaction_history.

data: DataFrame

Dataframe of price data including columns High, Low, Open, Close, Volume for each asset.

Both asset name and field make a multiindex column. The index should consist of order pandas timestamps.

assets: List[Asset]

Assets available to strategy.

strategies: List[Strategy]

Strategies to be called within this runner.

mandates: Dict[str, BookMandate]

Dictionary of asset mandates (experimental).

books: List[Book]

Books available to strategies.

If not supplied will be populated with single book named ‘Main’ denominated in USD.

run(params: Dict[str, Any] | None = None) StrategyRunnerResult[source]

Execute each strategy through time.

run_batch(params_iterable: Iterable[Dict[str, Any]], executor: ProcessPoolExecutor | None = None) List[StrategyRunnerResult][source]

Run a set of parameter combinations.

Order

class yabte.backtest.order.OrderStatus(value)[source]

Bases: Enum

Various statuses.

MANDATE_FAILED = 1

Order failed due to mandate.

CANCELLED = 2

Order was cancelled.

OPEN = 3

Order is open.

COMPLETE = 4

Order completed succesfully.

REPLACED = 5

Order was replaced.

class yabte.backtest.order.OrderSizeType(value)[source]

Bases: Enum

Various size types.

QUANTITY = 1

Size is a quantity.

NOTIONAL = 2

Size represent notional amount.

BOOK_PERCENT = 3

Size is a percentage of book value.

class yabte.backtest.order.Order(*, status: ~yabte.backtest.order.OrderStatus = OrderStatus.OPEN, book: str | ~yabte.backtest.book.Book | None = None, suborders: ~typing.List[~yabte.backtest.order.Order] = <factory>, label: str | None = None, priority: int = 0, key: str | None = None)[source]

Bases: object

Base class for all orders.

status: OrderStatus = 3

Status of order.

book: str | Book | None = None

Target book.

suborders: List[Order]

Additional orders to be executed the following timestep.

label: str | None = None

Label to assist in matching / filtering.

priority: int = 0

Each day orders are sorted by this field and executed in order.

key: str | None = None

Unique key for this order.

If a key is set then only the newest order with this key is kept. Older orders with the same key will be removed.

post_complete(trades: List[Trade])[source]

Called after and with trades that have been successfully booked.

It can append new orders to suborders for execution in the following timestep.

apply(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Applies order to self.book for time ts using provided day_data and dictionary of asset information asset_map.

class yabte.backtest.order.Orders[source]

Bases: object

Double ended queue of orders.

sort_by_priority()[source]

Sorts orders by order priority.

remove_duplicate_keys() List[Order][source]

Remove older orders with same key.

Returns a list of orders than were removed with status set to REPLACED.

class yabte.backtest.order.SimpleOrder(*, status: ~yabte.backtest.order.OrderStatus = OrderStatus.OPEN, book: str | ~yabte.backtest.book.Book | None = None, suborders: ~typing.List[~yabte.backtest.order.Order] = <factory>, label: str | None = None, priority: int = 0, key: str | None = None, asset_name: str, size: ~decimal.Decimal, size_type: ~yabte.backtest.order.OrderSizeType = OrderSizeType.QUANTITY)[source]

Bases: Order

Simple market order.

asset_name: str

Asset name for order.

size: Decimal

Order size.

size_type: OrderSizeType = 1

Order size type.

Can be a quantity, notional or book percent.

pre_execute_check(ts: Timestamp, trade_price: Decimal) OrderStatus | None[source]

Called with the current timestep and calculated trade price before the trade is executed.

If it returns None, the trade is executed as normal. It can return OrderStatus.CANCELLED to indicate the trade should be cancelled or OrderStatus.OPEN to indicate the trade should not be executed in the current timestep and processed in the following timestep.

apply(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Applies order to self.book for time ts using provided day_data and dictionary of asset information asset_map.

book: str | Book | None = None

Target book.

key: str | None = None

Unique key for this order.

If a key is set then only the newest order with this key is kept. Older orders with the same key will be removed.

label: str | None = None

Label to assist in matching / filtering.

post_complete(trades: List[Trade])

Called after and with trades that have been successfully booked.

It can append new orders to suborders for execution in the following timestep.

priority: int = 0

Each day orders are sorted by this field and executed in order.

status: OrderStatus = 3

Status of order.

suborders: List[Order]

Additional orders to be executed the following timestep.

class yabte.backtest.order.PositionalOrderCheckType(value)[source]

Bases: Enum

An enumeration.

class yabte.backtest.order.PositionalOrder(*, status: ~yabte.backtest.order.OrderStatus = OrderStatus.OPEN, book: str | ~yabte.backtest.book.Book | None = None, suborders: ~typing.List[~yabte.backtest.order.Order] = <factory>, label: str | None = None, priority: int = 0, key: str | None = None, asset_name: str, size: ~decimal.Decimal, size_type: ~yabte.backtest.order.OrderSizeType = OrderSizeType.QUANTITY, check_type: ~yabte.backtest.order.PositionalOrderCheckType = PositionalOrderCheckType.POS_TQ_DIFFER)[source]

Bases: SimpleOrder

Ensures current position is size and will close out existing positions to achieve this.

book: str | Book | None = None

Target book.

key: str | None = None

Unique key for this order.

If a key is set then only the newest order with this key is kept. Older orders with the same key will be removed.

label: str | None = None

Label to assist in matching / filtering.

post_complete(trades: List[Trade])

Called after and with trades that have been successfully booked.

It can append new orders to suborders for execution in the following timestep.

pre_execute_check(ts: Timestamp, trade_price: Decimal) OrderStatus | None

Called with the current timestep and calculated trade price before the trade is executed.

If it returns None, the trade is executed as normal. It can return OrderStatus.CANCELLED to indicate the trade should be cancelled or OrderStatus.OPEN to indicate the trade should not be executed in the current timestep and processed in the following timestep.

priority: int = 0

Each day orders are sorted by this field and executed in order.

size_type: OrderSizeType = 1

Order size type.

Can be a quantity, notional or book percent.

status: OrderStatus = 3

Status of order.

asset_name: str

Asset name for order.

size: Decimal

Order size.

suborders: List[Order]

Additional orders to be executed the following timestep.

check_type: PositionalOrderCheckType = 1

Condition type to determine if a trade is required.

apply(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Applies order to self.book for time ts using provided day_data and dictionary of asset information asset_map.

class yabte.backtest.order.BasketOrder(asset_names: ~typing.List[str], weights: ~typing.List[~decimal.Decimal], size: ~decimal.Decimal, size_type: ~yabte.backtest.order.OrderSizeType = OrderSizeType.QUANTITY, *, status: ~yabte.backtest.order.OrderStatus = OrderStatus.OPEN, book: str | ~yabte.backtest.book.Book | None = None, suborders: ~typing.List[~yabte.backtest.order.Order] = <factory>, label: str | None = None, priority: int = 0, key: str | None = None)[source]

Bases: Order

Combine multiple assets into a single order.

book: str | Book | None = None

Target book.

key: str | None = None

Unique key for this order.

If a key is set then only the newest order with this key is kept. Older orders with the same key will be removed.

label: str | None = None

Label to assist in matching / filtering.

post_complete(trades: List[Trade])

Called after and with trades that have been successfully booked.

It can append new orders to suborders for execution in the following timestep.

priority: int = 0

Each day orders are sorted by this field and executed in order.

status: OrderStatus = 3

Status of order.

suborders: List[Order]

Additional orders to be executed the following timestep.

asset_names: List[str]

List of asset names in basket.

weights: List[Decimal]

Corresponding weights for each asset.

size: Decimal

Combined size of order.

size_type: OrderSizeType = 1

Size type.

apply(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Applies order to self.book for time ts using provided day_data and dictionary of asset information asset_map.

class yabte.backtest.order.PositionalBasketOrder(asset_names: ~typing.List[str], weights: ~typing.List[~decimal.Decimal], size: ~decimal.Decimal, size_type: ~yabte.backtest.order.OrderSizeType = OrderSizeType.QUANTITY, *, status: ~yabte.backtest.order.OrderStatus = OrderStatus.OPEN, book: str | ~yabte.backtest.book.Book | None = None, suborders: ~typing.List[~yabte.backtest.order.Order] = <factory>, label: str | None = None, priority: int = 0, key: str | None = None, check_type: ~yabte.backtest.order.PositionalOrderCheckType = PositionalOrderCheckType.POS_TQ_DIFFER)[source]

Bases: BasketOrder

Similar to a BasketOrder but will close out existing positions if they do not match requested weights.

book: str | Book | None = None

Target book.

key: str | None = None

Unique key for this order.

If a key is set then only the newest order with this key is kept. Older orders with the same key will be removed.

label: str | None = None

Label to assist in matching / filtering.

post_complete(trades: List[Trade])

Called after and with trades that have been successfully booked.

It can append new orders to suborders for execution in the following timestep.

priority: int = 0

Each day orders are sorted by this field and executed in order.

size_type: OrderSizeType = 1

Size type.

status: OrderStatus = 3

Status of order.

asset_names: List[str]

List of asset names in basket.

weights: List[Decimal]

Corresponding weights for each asset.

size: Decimal

Combined size of order.

suborders: List[Order]

Additional orders to be executed the following timestep.

apply(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Applies order to self.book for time ts using provided day_data and dictionary of asset information asset_map.

Transaction

class yabte.backtest.transaction.Transaction(*, ts: Timestamp, total: Decimal = Decimal('0'), desc: str = '')[source]

Bases: object

A frozen record of a transaction.

ts: Timestamp

Transaction time.

total: Decimal = Decimal('0')

Total transaction value.

Negative values are costs and positive values are benefits.

desc: str = ''

Description.

class yabte.backtest.transaction.CashTransaction(*, ts: Timestamp, total: Decimal = Decimal('0'), desc: str = '')[source]

Bases: Transaction

A frozen record of a cash transaction.

desc: str = ''

Description.

total: Decimal = Decimal('0')

Total transaction value.

Negative values are costs and positive values are benefits.

ts: Timestamp

Transaction time.

class yabte.backtest.transaction.Trade(*, ts: pd.Timestamp, total: Decimal = Decimal('0'), desc: str = '', quantity: Decimal, price: Decimal, asset_name: AssetName, order_label: str | None)[source]

Bases: Transaction

A frozen record of a trade transaction.

A negative quantity represents a sell trade and a positive quantity represents a buy trade.

quantity: Decimal

Traded quantity.

desc: str = ''

Description.

total: Decimal = Decimal('0')

Total transaction value.

Negative values are costs and positive values are benefits.

ts: pd.Timestamp

Transaction time.

price: Decimal

Traded price.

asset_name: AssetName

Traded asset.

order_label: str | None

Associated Order label.

Book

class yabte.backtest.book.BookMandate[source]

Bases: object

yabte.backtest.book.BookName

Book name string.

class yabte.backtest.book.Book(*, name: str, mandates: ~typing.Dict[str, ~yabte.backtest.book.BookMandate] = <factory>, positions: ~typing.Dict[str, ~decimal.Decimal] = <factory>, transactions: ~typing.List[~yabte.backtest.transaction.Transaction] = <factory>, denom: str = 'USD', cash: ~decimal.Decimal = Decimal('0'), rate: ~decimal.Decimal = Decimal('0'), interest_round_dp: int = 3, _history: ~typing.List[~typing.List[~typing.Any]] = <factory>)[source]

Bases: object

Record of asset trades and positions including cash.

A name can be provided and the default cash value can be changed to a non-zero amount.

name: str

Name of book.

mandates: Dict[str, BookMandate]

Dictionary mapping assets to mandates (experimental).

positions: Dict[str, Decimal]

Dictionary tracking positions of each asset.

transactions: List[Transaction]

List of executed trades.

denom: str = 'USD'

Book currency.

cash: Decimal = Decimal('0')

Cash value of book.

rate: Decimal = Decimal('0')

Daily interest rate applied to cash at end of day.

interest_round_dp: int = 3

Number of decimal places to round interest.

property history: DataFrame

Dataframe with book cash, mtm and total value history.

test_trades(trades: Sequence[Trade]) bool[source]

Checks whether list of trades will be successful by not failing any mandates.

add_transactions(transactions: Sequence[Transaction])[source]

Records the transactions and adjusts internal dictionary of positions and value of cash accordingly.

eod_tasks(ts: Timestamp, day_data: Series, asset_map: Dict[str, Asset])[source]

Run end of day tasks such as book keeping.

Asset

yabte.backtest.asset.AssetName

Asset name string.

class yabte.backtest.asset.Asset(*, name: str, denom: str = 'USD', price_round_dp: int = 2, quantity_round_dp: int = 2, data_label: str | None = None)[source]

Bases: object

Anything that has a price.

name: str

Name string.

denom: str = 'USD'

Denominated currency.

price_round_dp: int = 2

Number of decimal places to round prices to.

quantity_round_dp: int = 2

Number of decimal places to round quantities to.

data_label: str | None = None

StrategyRunner.data column index 1st level label.

Defaults to name

round_quantity(quantity) Decimal[source]

Round quantity.

intraday_traded_price(asset_day_data: Series, size: Decimal | None = None) Decimal[source]

Calculate price during market hours with given row of asset_day_data and the order size.

The size can be used to determine a price from say, bid / ask spreads.

end_of_day_price(asset_day_data: Series) Decimal[source]

Calculate price at end of day with given row of asset_day_data.

check_and_fix_data(data: DataFrame) DataFrame[source]

Checks dataframe data has correct fields and fixes columns where necessary.

data_fields() list[tuple[str, int]][source]

List of data fields and their availability.

class yabte.backtest.asset.OHLCAsset(*, name: str, denom: str = 'USD', price_round_dp: int = 2, quantity_round_dp: int = 2, data_label: str | None = None)[source]

Bases: Asset

Assets whose price history is represented by High, Low, Open, Close and Volume fields.

data_label: str | None = None

StrategyRunner.data column index 1st level label.

Defaults to name

denom: str = 'USD'

Denominated currency.

price_round_dp: int = 2

Number of decimal places to round prices to.

quantity_round_dp: int = 2

Number of decimal places to round quantities to.

round_quantity(quantity) Decimal

Round quantity.

name: str

Name string.

data_fields() list[tuple[str, int]][source]

List of data fields and their availability.

intraday_traded_price(asset_day_data: Series, size: Decimal | None = None) Decimal[source]

Calculate price during market hours with given row of asset_day_data and the order size.

The size can be used to determine a price from say, bid / ask spreads.

end_of_day_price(asset_day_data: Series) Decimal[source]

Calculate price at end of day with given row of asset_day_data.

check_and_fix_data(data: DataFrame) DataFrame[source]

Checks dataframe data has correct fields and fixes columns where necessary.