bbstrader.btengine package

Module contents

Overview

This Backtesting Module provides a comprehensive suite of tools to test trading strategies in an event-driven system. It simulates the execution of trades in historical market conditions to evaluate the performance of trading strategies before applying them in live trading environments. Designed with modularity and extensibility in mind, it caters to both novices and experts in algorithmic trading.

Features

  • Event-Driven Architecture: Processes market data, generates signals, executes orders, and manages portfolio updates in response to events, closely mimicking live trading environments.

  • Historical Market Data Support: Utilizes historical OHLCV data from CSV files, Yahoo finance and MT5 terminal allowing for the testing of strategies over various market conditions and time frames.

  • Performance Metrics Calculation: Includes tools for calculating key performance indicators, such as Sharpe Ratio, Sortino Ratio, and drawdowns, to evaluate the effectiveness of trading strategies.

  • Visualization: Generates plots of the equity curve, returns, drawdowns, and other metrics for comprehensive strategy performance analysis.

Components

  • BacktestEgine: Orchestrates the backtesting process, managing events and invoking components.

  • Event: Abstract class for events, with implementations for market data, signals, fill and order events.

  • DataHandler: Abstract class for market data handling, with an implementation for CSVDataHandler, MT5DataHandler, YFDataHandler. We will add another data handling in the future such as MacroEconomic Data, Fundamental Data, TICK Data and Real-time Data.

  • Portfolio: Manages positions and calculates performance metrics, responding to market data and signals.

  • ExecutionHandler: Abstract class for order execution, with a simulated execution handler provided with an implementation for SimExecutionHandler.

  • Performance: Utility functions for calculating performance metrics and visualizing strategy performance.

Examples

>>> from bbstrader.btengine import run_backtest
>>> from datetime import datetime
>>> run_backtest(
...     symbol_list=['AAPL', 'GOOG'],
...     start_date=datetime(2020, 1, 1),
...     data_handler=DataHandler,
...     strategy=Strategy,
...     exc_handler=ExecutionHandler,
...     initial_capital=500000.0,
...     heartbeat=1.0
... )

Notes

See bbstrader.btengine.backtest.run_backtest for more details on the backtesting process and its parameters.

Submodules

bbstrader.btengine.backtest module

class bbstrader.btengine.backtest.BacktestEngine(symbol_list: List[str], initial_capital: float, heartbeat: float, start_date: datetime, data_handler: Type[DataHandler], execution_handler: Type[ExecutionHandler], strategy: Type[Strategy], /, **kwargs: Any)[source]

Bases: object

The BacktestEngine() object encapsulates the event-handling logic and essentially ties together all of the other classes.

The BacktestEngine object is designed to carry out a nested while-loop event-driven system in order to handle the events placed on the Event Queue object. The outer while-loop is known as the “heartbeat loop” and decides the temporal resolution of the backtesting system. In a live environment this value will be a positive number, such as 600 seconds (every ten minutes). Thus the market data and positions will only be updated on this timeframe.

For the backtester described here the “heartbeat” can be set to zero, irrespective of the strategy frequency, since the data is already available by virtue of the fact it is historical! We can run the backtest at whatever speed we like, since the event-driven system is agnostic to when the data became available, so long as it has an associated timestamp.

The inner while-loop actually processes the signals and sends them to the correct component depending upon the event type. Thus the Event Queue is continually being populated and depopulated with events. This is what it means for a system to be event-driven.

The initialisation of the BacktestEngine object requires the full symbol list of traded symbols, the initial capital, the heartbeat time in milliseconds, the start datetime stamp of the backtest as well as the DataHandler, ExecutionHandler, Strategy objects and additionnal kwargs based on the ExecutionHandler, the DataHandler, and the Strategy used.

A Queue is used to hold the events. The signals, orders and fills are counted. For a MarketEvent, the Strategy object is told to recalculate new signals, while the Portfolio object is told to reindex the time. If a SignalEvent object is received the Portfolio is told to handle the new signal and convert it into a set of OrderEvents, if appropriate. If an OrderEvent is received the ExecutionHandler is sent the order to be transmitted to the broker (if in a real trading setting). Finally, if a FillEvent is received, the Portfolio will update itself to be aware of the new positions.

simulate_trading() DataFrame[source]

Simulates the backtest and outputs portfolio performance.

Returns:

The portfilio values over time (capital, equity, returns etc.)

Return type:

pd.DataFrame

bbstrader.btengine.backtest.run_backtest(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], exc_handler: Type[ExecutionHandler] | None = None, initial_capital: float = 100000.0, heartbeat: float = 0.0, **kwargs: Any) DataFrame[source]

Runs a backtest simulation based on a DataHandler, Strategy, and ExecutionHandler.

Parameters:
  • symbol_list (List[str]) – List of symbol strings for the assets to be backtested.

  • start_date (datetime) – Start date of the backtest.

  • data_handler (DataHandler) – A subclass of the DataHandler class, responsible for managing and processing market data. Available options include CSVDataHandler, MT5DataHandler, and YFDataHandler.

  • strategy (Strategy) –

    The trading strategy to be employed during the backtest. The strategy must be a subclass of Strategy and should include the following attributes: - bars (DataHandler): The DataHandler class for the strategy. - events (Queue): Queue instance for managing events. - symbol_list (List[str]): List of symbols to trade. - mode (str): ‘live’ or ‘backtest’.

    Additional parameters specific to the strategy should be passed in **kwargs. The strategy class must implement a calculate_signals method to generate SignalEvent.

  • exc_handler (ExecutionHandler, optional) – The execution handler for managing order executions. If not provided, a SimulatedExecutionHandler will be used by default. This handler must implement an execute_order method to process OrderEvent in the Backtest class.

  • initial_capital (float, optional) – The initial capital for the portfolio in the backtest. Default is 100,000.

  • heartbeat (float, optional) – Time delay (in seconds) between iterations of the event-driven backtest loop. Default is 0.0, allowing the backtest to run as fast as possible. This could also be used as a time frame in live trading (e.g., 1m, 5m, 15m) with a live DataHandler.

  • **kwargs – Additional parameters passed to the Backtest instance, which may include strategy-specific, data handler, portfolio, or execution handler options.

Returns:

The portfolio values over time (capital, equities, returns etc.).

Return type:

pd.DataFrame

Notes

This function generates three outputs:
  • A performance summary saved as an HTML file.

  • An equity curve of the portfolio saved as a CSV file.

  • Monthly returns saved as a PNG image.

Example

>>> from bbstrader.trading.strategies import StockIndexSTBOTrading
>>> from bbstrader.metatrader.utils import config_logger
>>> from bbstrader.datahandlers import MT5DataHandler
>>> from bbstrader.execution import MT5ExecutionHandler
>>> from datetime import datetime
>>>
>>> logger = config_logger('index_trade.log', console_log=True)
>>> symbol_list = ['[SP500]', 'GERMANY40', '[DJI30]', '[NQ100]']
>>> start = datetime(2010, 6, 1, 2, 0, 0)
>>> kwargs = {
...     'expected_returns': {'[NQ100]': 1.5, '[SP500]': 1.5, '[DJI30]': 1.0, 'GERMANY40': 1.0},
...     'quantities': {'[NQ100]': 15, '[SP500]': 30, '[DJI30]': 5, 'GERMANY40': 10},
...     'max_trades': {'[NQ100]': 3, '[SP500]': 3, '[DJI30]': 3, 'GERMANY40': 3},
...     'mt5_start': start,
...     'time_frame': '15m',
...     'strategy_name': 'SISTBO',
... }
>>> run_backtest(
...     symbol_list=symbol_list,
...     start_date=start,
...     data_handler=MT5DataHandler,
...     strategy=StockIndexSTBOTrading,
...     exc_handler=MT5ExecutionHandler,
...     initial_capital=100000.0,
...     heartbeat=0.0,
...     **kwargs
... )

bbstrader.btengine.data module

class bbstrader.btengine.data.CSVDataHandler(events: Queue[MarketEvent], symbol_list: List[str], **kwargs: Any)[source]

Bases: BaseCSVDataHandler

CSVDataHandler is designed to read CSV files for each requested symbol from disk and provide an interface to obtain the “latest” bar in a manner identical to a live trading interface.

This class is useful when you have your own data or you want to cutomize specific data in some form based on your Strategy() .

class bbstrader.btengine.data.DataHandler[source]

Bases: object

One of the goals of an event-driven trading system is to minimise duplication of code between the backtesting element and the live execution element. Ideally it would be optimal to utilise the same signal generation methodology and portfolio management components for both historical testing and live trading. In order for this to work the Strategy object which generates the Signals, and the Portfolio object which provides Orders based on them, must utilise an identical interface to a market feed for both historic and live running.

This motivates the concept of a class hierarchy based on a DataHandler object, which givesall subclasses an interface for providing market data to the remaining components within thesystem. In this way any subclass data handler can be “swapped out”, without affecting strategy or portfolio calculation.

Specific example subclasses could include HistoricCSVDataHandler, YFinanceDataHandler, FMPDataHandler, IBMarketFeedDataHandler etc.

property data: Dict[str, DataFrame]
abstractmethod get_latest_bar(symbol: str) Series[source]

Returns the last bar updated.

abstractmethod get_latest_bar_datetime(symbol: str) datetime | Timestamp[source]

Returns a Python datetime object for the last bar.

abstractmethod get_latest_bar_value(symbol: str, val_type: str) float[source]

Returns one of the Open, High, Low, Close, Adj Close, Volume or Returns from the last bar.

abstractmethod get_latest_bars(symbol: str, N: int = 1, df: bool = True) DataFrame | List[Series][source]

Returns the last N bars updated.

abstractmethod get_latest_bars_values(symbol: str, val_type: str, N: int = 1) ndarray[tuple[Any, ...], dtype[_ScalarT]][source]

Returns the last N bar values from the latest_symbol list, or N-k if less available.

property index: str | List[str]
property labels: List[str]
property symbols: List[str]
abstractmethod update_bars() None[source]

Pushes the latest bars to the bars_queue for each symbol in a tuple OHLCVI format: (datetime, Open, High, Low, Close, Adj Close, Volume, Retruns).

class bbstrader.btengine.data.EODHDataHandler(events: Queue[MarketEvent], symbol_list: List[str], **kwargs: Any)[source]

Bases: BaseCSVDataHandler

Downloads historical data from EOD Historical Data. Data is fetched using the eodhd library.

To use this class, you need to sign up for an API key at https://eodhistoricaldata.com/ and provide the key as an argument.

class bbstrader.btengine.data.FMPDataHandler(events: Queue[MarketEvent], symbol_list: List[str], **kwargs: Any)[source]

Bases: BaseCSVDataHandler

Downloads historical data from Financial Modeling Prep (FMP). Data is fetched using the financetoolkit library.

To use this class, you need to sign up for an API key at https://financialmodelingprep.com/developer/docs/pricing and provide the key as an argument.

class bbstrader.btengine.data.MT5DataHandler(events: Queue[MarketEvent], symbol_list: List[str], **kwargs: Any)[source]

Bases: BaseCSVDataHandler

Downloads historical data from MetaTrader 5 (MT5) and provides an interface for accessing this data bar-by-bar, simulating a live market feed for backtesting.

Data is downloaded from MT5, saved as CSV files, and then loaded using the functionality inherited from BaseCSVDataHandler.

This class is useful when you need to get data from specific broker for different time frames.

class bbstrader.btengine.data.YFDataHandler(events: Queue[MarketEvent], symbol_list: List[str], **kwargs: Any)[source]

Bases: BaseCSVDataHandler

Downloads historical data from Yahoo Finance and provides an interface for accessing this data bar-by-bar, simulating a live market feed for backtesting.

Data is fetched using the yfinance library and optionally cached to disk to speed up subsequent runs.

This class is useful when working with historical daily prices.

bbstrader.btengine.event module

class bbstrader.btengine.event.Event[source]

Bases: object

Event is base class providing an interface for all subsequent (inherited) events, that will trigger further events in the trading infrastructure. Since in many implementations the Event objects will likely develop greater complexity, it is thus being “future-proofed” by creating a class hierarchy. The Event class is simply a way to ensure that all events have a common interface and can be handled in a consistent manner.

class bbstrader.btengine.event.Events(*values)[source]

Bases: Enum

FILL = 'FILL'
MARKET = 'MARKET'
ORDER = 'ORDER'
SIGNAL = 'SIGNAL'
class bbstrader.btengine.event.FillEvent(timeindex: datetime, symbol: str, exchange: str, quantity: int | float, direction: Literal['BUY', 'SELL'], fill_cost: int | float | None, commission: float | None = None, order: str | None = None)[source]

Bases: Event

When an ExecutionHandler receives an OrderEvent it must transact the order. Once an order has been transacted it generates a FillEvent, which describes the cost of purchase or sale as well as the transaction costs, such as fees or slippage.

The FillEvent is the Event with the greatest complexity. It contains a timestamp for when an order was filled, the symbol of the order and the exchange it was executed on, the quantity of shares transacted, the actual price of the purchase and the commission incurred.

The commission is calculated using the Interactive Brokers commissions. For US API orders this commission is 1.30 USD minimum per order, with a flat rate of either 0.013 USD or 0.08 USD per share depending upon whether the trade size is below or above 500 units of stock.

calculate_ib_commission() float[source]

Calculates the fees of trading based on an Interactive Brokers fee structure for API, in USD. This does not include exchange or ECN fees. Based on “US API Directed Orders”: https://www.interactivebrokers.com/en/index.php?f=commission&p=stocks2

commission: float
class bbstrader.btengine.event.MarketEvent[source]

Bases: Event

Market Events are triggered when the outer while loop of the backtesting system begins a new “heartbeat”. It occurs when the DataHandler object receives a new update of market data for any symbols which are currently being tracked. It is used to trigger the Strategy object generating new trading signals. The event object simply contains an identification that it is a market event, with no other structure.

class bbstrader.btengine.event.OrderEvent(symbol: str, order_type: Literal['MKT', 'LMT', 'STP', 'STPLMT'], quantity: int | float, direction: Literal['BUY', 'SELL'], price: int | float | None = None, signal: str | None = None)[source]

Bases: Event

When a Portfolio object receives SignalEvents it assesses them in the wider context of the portfolio, in terms of risk and position sizing. This ultimately leads to OrderEvents that will be sent to an ExecutionHandler.

The OrderEvents is slightly more complex than a SignalEvents since it contains a quantity field in addition to the aforementioned properties of SignalEvent. The quantity is determined by the Portfolio constraints. In addition the OrderEvent has a print_order() method, used to output the information to the console if necessary.

print_order() None[source]

Outputs the values within the Order.

class bbstrader.btengine.event.SignalEvent(strategy_id: int, symbol: str, datetime: datetime, signal_type: Literal['LONG', 'SHORT', 'EXIT'], quantity: int | float = 100, strength: int | float = 1.0, price: int | float | None = None, stoplimit: int | float | None = None)[source]

Bases: Event

The Strategy object utilises market data to create new SignalEvents. The SignalEvent contains a strategy ID, a ticker symbol, a timestamp for when it was generated, a direction (long or short) and a “strength” indicator (this is useful for mean reversion strategies) and the quantiy to buy or sell. The SignalEvents are utilised by the Portfolio object as advice for how to trade.

bbstrader.btengine.execution module

class bbstrader.btengine.execution.ExecutionHandler[source]

Bases: object

The ExecutionHandler abstract class handles the interaction between a set of order objects generated by a Portfolio and the ultimate set of Fill objects that actually occur in the market.

The handlers can be used to subclass simulated brokerages or live brokerages, with identical interfaces. This allows strategies to be backtested in a very similar manner to the live trading engine.

The ExecutionHandler described here is exceedingly simple, since it fills all orders at the current market price. This is highly unrealistic, for other markets thant CFDs but serves as a good baseline for improvement.

abstractmethod execute_order(event: OrderEvent) None[source]

Takes an Order event and executes it, producing a Fill event that gets placed onto the Events queue.

Parameters:

event (OrderEvent) – Contains an Event object with order information.

class bbstrader.btengine.execution.MT5ExecutionHandler(events: Queue[FillEvent | OrderEvent], data: DataHandler, **kwargs: Any)[source]

Bases: ExecutionHandler

The main role of MT5ExecutionHandler class is to estimate the execution fees for different asset classes on the MT5 terminal.

Generally we have four types of fees when we execute trades using the MT5 terminal (commissions, swap, spread and other fees). But most of these fees depend on the specifications of each instrument and the duration of the transaction for the swap for example.

Calculating the exact fees for each instrument would be a bit complex because our Backtest engine and the Portfolio class do not take into account the duration of each trade to apply the appropriate rate for the swap for example. So we have to use only the model of calculating the commissions for each asset class and each instrument.

The second thing that must be taken into account on MT5 is the type of account offered by the broker. Brokers have different account categories each with its specifications for each asset class and each instrument. Again considering all these conditions would make our class very complex. So we took the Raw Spread account fee calculation model from [Just Market](https://one.justmarkets.link/a/tufvj0xugm/registration/trader) for indicies, forex, commodities and crypto. We used the [Admiral Market](https://cabinet.a-partnership.com/visit/?bta=35537&brand=admiralmarkets) account fee calculation model from Trade.MT5 account type for stocks and ETFs.

Note

This class only works with bbstrader.metatrader.data.MT5DataHandler class.

execute_order(event: OrderEvent) None[source]

Executes an Order event by converting it into a Fill event.

Parameters:

event (OrderEvent) – Contains an Event object with order information.

class bbstrader.btengine.execution.SimExecutionHandler(events: Queue[FillEvent | OrderEvent], data: DataHandler, **kwargs: Any)[source]

Bases: ExecutionHandler

The simulated execution handler simply converts all order objects into their equivalent fill objects automatically without latency, slippage or fill-ratio issues.

This allows a straightforward “first go” test of any strategy, before implementation with a more sophisticated execution handler.

execute_order(event: OrderEvent) None[source]

Simply converts Order objects into Fill objects naively, i.e. without any latency, slippage or fill ratio problems.

Parameters:

event (OrderEvent) – Contains an Event object with order information.

bbstrader.btengine.performance module

bbstrader.btengine.performance.create_drawdowns(pnl: Series) Tuple[Series, float, float][source]

Calculate the largest peak-to-trough drawdown of the PnL curve as well as the duration of the drawdown. Requires that the pnl_returns is a pandas Series.

Parameters:

pnl – A pandas Series representing period percentage returns.

Returns:

drawdown, duration - high-water mark, duration.

Return type:

(tuple)

bbstrader.btengine.performance.create_sharpe_ratio(returns: Series, periods: int = 252) float[source]

Create the Sharpe ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).

Parameters:
  • returns – A pandas Series representing period percentage returns.

  • periods (int) – Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc.

Returns:

Sharpe ratio

Return type:

S (float)

bbstrader.btengine.performance.create_sortino_ratio(returns: Series, periods: int = 252) float[source]

Create the Sortino ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).

Parameters:
  • returns – A pandas Series representing period percentage returns.

  • periods (int) – Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc.

Returns:

Sortino ratio

Return type:

S (float)

bbstrader.btengine.performance.get_asset_performances(portfolio: DataFrame, assets: List[str], plot: bool = True, strategy: str = '') Series[source]

Calculate the performance of the assets in the portfolio.

Parameters:
  • portfolio (pd.DataFrame) – The portfolio DataFrame.

  • assets (List[str]) – The list of assets to calculate the performance for.

  • plot (bool) – Whether to plot the performance of the assets.

  • strategy (str) – The name of the strategy.

Returns:

The performance of the assets.

Return type:

pd.Series

bbstrader.btengine.performance.get_perfbased_weights(performances: Series) Dict[str, float][source]

Calculate the weights of the assets based on their performances.

Parameters:

performances (pd.Series) – The performances of the assets.

Returns:

The weights of the assets.

Return type:

Dict[str, float]

bbstrader.btengine.performance.plot_monthly_yearly_returns(df: DataFrame, title: str) None[source]

Plot the monthly and yearly returns of the strategy.

Parameters:
  • df (pd.DataFrame)

  • drawdowns. (The DataFrame containing the strategy returns and)

  • title (str) – The title of the plot.

Note: The DataFrame should contain the following columns: - Datetime : The timestamp of the data - Equity Curve : The portfolio value - Returns : The period returns - Drawdown : The drawdowns - Total : The total returns

bbstrader.btengine.performance.plot_performance(df: DataFrame, title: str) None[source]
Plot the performance of the strategy:
  • (Portfolio value, %)

  • (Period returns, %)

  • (Drawdowns, %)

Parameters:
  • df (pd.DataFrame)

  • drawdowns. (The DataFrame containing the strategy returns and)

  • title (str) – The title of the plot.

Note: The DataFrame should contain the following columns - Datetime: The timestamp of the data - Equity Curve: The portfolio value - Returns: The period returns - Drawdown: The drawdowns - Total : The total returns

bbstrader.btengine.performance.plot_returns_and_dd(df: DataFrame, benchmark: str, title: str) None[source]

Plot the returns and drawdowns of the strategy compared to a benchmark.

Parameters:
  • df (pd.DataFrame) – The DataFrame containing the strategy returns and drawdowns.

  • benchmark (str) – The ticker symbol of the benchmark to compare the strategy to.

  • title (str) – The title of the plot.

Note: The DataFrame should contain the following columns: - Datetime : The timestamp of the data - Equity Curve : The portfolio value - Returns : The period returns - Drawdown : The drawdowns - Total : The total returns

bbstrader.btengine.performance.show_qs_stats(returns: Series, benchmark: str, strategy_name: str, save_dir: str | None = None) None[source]

Generate the full quantstats report for the strategy.

Parameters:
  • returns (pd.Serie) – The DataFrame containing the strategy returns and drawdowns.

  • benchmark (str) – The ticker symbol of the benchmark to compare the strategy to.

  • strategy_name (str) – The name of the strategy.

bbstrader.btengine.portfolio module

class bbstrader.btengine.portfolio.Portfolio(bars: DataHandler, events: Queue[OrderEvent | FillEvent | SignalEvent], start_date: datetime, initial_capital: float = 100000.0, **kwargs: Any)[source]

Bases: object

This describes a Portfolio() object that keeps track of the positions within a portfolio and generates orders of a fixed quantity of stock based on signals.

The portfolio order management system is possibly the most complex component of an event driven backtester. Its role is to keep track of all current market positions as well as the market value of the positions (known as the “holdings”). This is simply an estimate of the liquidation value of the position and is derived in part from the data handling facility of the backtester.

In addition to the positions and holdings management the portfolio must also be aware of risk factors and position sizing techniques in order to optimise orders that are sent to a brokerage or other form of market access.

Unfortunately, Portfolio and Order Management Systems (OMS) can become rather complex! So let’s keep the Portfolio object relatively straightforward anf improve it foward.

Continuing in the vein of the Event class hierarchy a Portfolio object must be able to handle SignalEvent objects, generate OrderEvent objects and interpret FillEvent objects to update positions. Thus it is no surprise that the Portfolio objects are often the largest component of event-driven systems, in terms of lines of code (LOC).

The initialisation of the Portfolio object requires access to the bars DataHandler, the Event Queue, a start datetime stamp and an initial capital value (defaulting to 100,000 USD) and others parameter based on the Strategy requirement.

The Portfolio is designed to handle position sizing and current holdings, but will carry out trading orders by simply them to the brokerage with a predetermined fixed quantity size, if the portfolio has enough cash to place the order.

The portfolio contains the all_positions and current_positions members. The former stores a list of all previous positions recorded at the timestamp of a market data event. A position is simply the quantity of the asset held. Negative positions mean the asset has been shorted.

The latter current_positions dictionary stores contains the current positions for the last market bar update, for each symbol.

In addition to the positions data the portfolio stores holdings, which describe the current market value of the positions held. “Current market value” in this instance means the closing price obtained from the current market bar, which is clearly an approximation, but is reasonable enough for the time being. all_holdings stores the historical list of all symbol holdings, while current_holdings stores the most up to date dictionary of all symbol holdings values.

construct_all_holdings() List[Dict[str, Any]][source]

Constructs the holdings list using the start_date to determine when the time index will begin.

construct_all_positions() List[Dict[str, Any]][source]

Constructs the positions list using the start_date to determine when the time index will begin.

construct_current_holdings() Dict[str, float][source]

This constructs the dictionary which will hold the instantaneous value of the portfolio across all symbols.

create_equity_curve_dataframe() None[source]

Creates a pandas DataFrame from the all_holdings list of dictionaries.

generate_order(signal: SignalEvent) OrderEvent | None[source]

Turns a SignalEvent into an OrderEvent.

Parameters:

signal (SignalEvent) – The tuple containing Signal information.

Returns:

The OrderEvent to be executed.

Return type:

OrderEvent

output_summary_stats() List[Any][source]

Creates a list of summary statistics for the portfolio.

update_fill(event: FillEvent) None[source]

Updates the portfolio current positions and holdings from a FillEvent.

update_holdings_from_fill(fill: FillEvent) None[source]

Takes a Fill object and updates the holdings matrix to reflect the holdings value.

Parameters:

fill (FillEvent) – The Fill object to update the holdings with.

update_positions_from_fill(fill: FillEvent) None[source]

Takes a Fill object and updates the position matrix to reflect the new position.

Parameters:

fill (FillEvent) – The Fill object to update the positions with.

update_signal(event: SignalEvent) None[source]

Acts on a SignalEvent to generate new orders based on the portfolio logic.

update_timeindex(event: MarketEvent) None[source]

Adds a new record to the positions matrix for the current market data bar. This reflects the PREVIOUS bar, i.e. all current market data at this stage is known (OHLCV). Makes use of a MarketEvent from the events queue.

bbstrader.btengine.strategy module

class bbstrader.btengine.strategy.BacktestStrategy(events: Queue[SignalEvent | FillEvent], symbol_list: List[str], bars: DataHandler, **kwargs: Any)[source]

Bases: BaseStrategy

Strategy implementation specifically for Backtesting. Handles internal state for orders, positions, trades, and cash. Simulates order execution and pending orders.

buy_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to buy at a limit price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

buy_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a long position

See bbstrader.btengine.event.SignalEvent for more details on arguments.

buy_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to buy at a stop price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

buy_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to buy at a stop-limit price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

property cash: float

Returns the available cash (virtual or real).

check_pending_orders() None[source]

Check for pending orders and handle them accordingly.

close_positions(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Close a position or exit all positions

See bbstrader.btengine.event.SignalEvent for more details on arguments.

data: DataHandler
events: Queue[SignalEvent | FillEvent]
get_asset_values(symbol_list: List[str], window: int, value_type: str = 'returns', array: bool = True, **kwargs) Dict[str, ndarray | Series] | None[source]

Get the historical OHLCV value or returns or custum value based on the DataHandker of the assets in the symbol list.

Parameters:
  • bars – DataHandler for market data handling, required for backtest mode.

  • symbol_list – List of ticker symbols for the pairs trading strategy.

  • value_type – The type of value to get (e.g., returns, open, high, low, close, adjclose, volume).

  • array – If True, return the values as numpy arrays, otherwise as pandas Series.

  • mode – Mode of operation for the strategy.

  • window – The lookback period for resquesting the data.

  • tf – The time frame for the strategy.

  • error – The error handling method for the function.

Returns:

Historical values of the assets in the symbol list.

Return type:

asset_values

Note

In Live mode, the bbstrader.metatrader.rates.Rates class is used to get the historical data so the value_type must be ‘returns’, ‘open’, ‘high’, ‘low’, ‘close’, ‘adjclose’, ‘volume’.

get_update_from_portfolio(positions: Dict[str, float], holdings: Dict[str, float]) None[source]

Update the positions and holdings for the strategy from the portfolio.

Positions are the number of shares of a security that are owned in long or short. Holdings are the value (postions * price) of the security that are owned in long or short.

Parameters:
  • positions – The positions for the symbols in the strategy.

  • holdings – The holdings for the symbols in the strategy.

property holdings: Dict[str, float]
property orders: Dict[str, Dict[str, List[SignalEvent]]]
property positions: Dict[str, Dict[str, int | float]]
sell_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to sell at a limit price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

sell_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a short position

See bbstrader.btengine.event.SignalEvent for more details on arguments.

sell_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to sell at a stop price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

sell_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: datetime | Timestamp | None = None) None[source]

Open a pending order to sell at a stop-limit price

See bbstrader.btengine.event.SignalEvent for more details on arguments.

property trades: Dict[str, Dict[str, int]]
update_trades_from_fill(event: FillEvent) None[source]

This method updates the trades for the strategy based on the fill event. It is used to keep track of the number of trades executed for each order.