bbstrader.btengine¶
Event-driven backtesting engine, plus the research and realism toolkit: execution friction models, vectorized research fast-path, optimization/walk-forward, overfitting diagnostics, risk analytics, data catalog, strategy templates, multi-timeframe resampling, and the experiment store.
btengine ¶
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, anddrawdowns, to evaluate the effectiveness of trading strategies. - Visualization: Generates plots of the
equity curve,returns,drawdowns, and other metrics for comprehensive strategyperformance 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.
MonteCarloResult
dataclass
¶
MonteCarloResult(terminal_returns: NDArray[float64], bands: Dict[str, NDArray[float64]], horizon: int)
Monte Carlo simulated terminal-return distribution and equity bands.
quantile ¶
Return the q-quantile of the simulated terminal returns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
float
|
Quantile in the interval [0, 1]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The terminal return at quantile |
Source code in src/bbstrader/btengine/analytics.py
BacktestEngine ¶
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)
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.
Initialises the backtest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
The list of symbol strings. |
required |
intial_capital
|
float
|
The starting capital for the portfolio. |
required |
heartbeat
|
float
|
Backtest "heartbeat" in seconds |
required |
start_date
|
datetime
|
The start datetime of the strategy. |
required |
data_handler (DataHandler)
|
Handles the market data feed. |
required | |
execution_handler (ExecutionHandler)
|
Handles the orders/fills for trades. |
required | |
strategy
|
Strategy
|
Generates signals based on market data. |
required |
kwargs
|
Additional parameters based on the |
required |
Source code in src/bbstrader/btengine/backtest.py
simulate_trading ¶
Simulates the backtest and outputs portfolio performance.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: The portfilio values over time (capital, equity, returns etc.) |
Source code in src/bbstrader/btengine/backtest.py
DataCatalog ¶
A local OHLCV cache keyed by (source, symbol, timeframe).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_dir
|
Optional[str]
|
Root directory for the store. Defaults to
|
None
|
fmt
|
str
|
|
'auto'
|
Initialise the catalog and ensure its base directory exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_dir
|
Optional[str]
|
Root directory for the store. Defaults to
|
None
|
fmt
|
str
|
One of |
'auto'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/catalog.py
has ¶
metadata ¶
Return the stored metadata for the key, or None if absent.
Source code in src/bbstrader/btengine/catalog.py
is_fresh ¶
Return True if the cached dataset exists and is within max_age_days.
A max_age_days of None means "never expires" (any cached copy is
fresh); a value of 0 (or negative) means the cache is always stale,
independent of clock resolution.
Source code in src/bbstrader/btengine/catalog.py
get ¶
Load a cached dataset, or None if it is not present.
Source code in src/bbstrader/btengine/catalog.py
put ¶
put(df: DataFrame, source: str, symbol: str, timeframe: str, extra_meta: Optional[Dict[str, Any]] = None) -> Path
Persist df for the key and write a metadata sidecar.
Source code in src/bbstrader/btengine/catalog.py
fetch ¶
fetch(loader: Callable[[], DataFrame], source: str, symbol: str, timeframe: str = 'D1', max_age_days: Optional[float] = None, force: bool = False, extra_meta: Optional[Dict[str, Any]] = None) -> pd.DataFrame
Return cached data if fresh, otherwise call loader and cache it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loader
|
Callable[[], DataFrame]
|
Zero-argument callable returning a normalized OHLCV DataFrame
(only called on a cache miss or when |
required |
source
|
str
|
Logical source name (e.g. |
required |
symbol
|
str
|
Instrument symbol. |
required |
timeframe
|
str
|
Bar timeframe/period label used in the cache key. |
'D1'
|
max_age_days
|
Optional[float]
|
Maximum acceptable cache age; None means never expires. |
None
|
force
|
bool
|
Bypass the cache and always reload. |
False
|
Source code in src/bbstrader/btengine/catalog.py
list_datasets ¶
Return metadata for every dataset currently in the store.
Source code in src/bbstrader/btengine/catalog.py
DataHandler ¶
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.
get_latest_bar
abstractmethod
¶
get_latest_bars
abstractmethod
¶
Returns the last N bars updated.
Source code in src/bbstrader/btengine/data.py
get_latest_bar_datetime
abstractmethod
¶
Returns a Python datetime object for the last bar.
get_latest_bar_value
abstractmethod
¶
Returns one of the Open, High, Low, Close, Adj Close, Volume or Returns from the last bar.
Source code in src/bbstrader/btengine/data.py
get_latest_bars_values
abstractmethod
¶
Returns the last N bar values from the latest_symbol list, or N-k if less available.
Source code in src/bbstrader/btengine/data.py
update_bars
abstractmethod
¶
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).
Source code in src/bbstrader/btengine/data.py
CSVDataHandler ¶
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() .
Initialises the historic data handler by requesting
the location of the CSV files and a list of symbols.
It will be assumed that all files are of the form
symbol.csv, where symbol is a string in the list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue. |
required |
symbol_list
|
List[str]
|
A list of symbol strings. |
required |
csv_dir
|
str
|
Absolute directory path to the CSV files. |
required |
NOTE: All csv fille can be stored in 'Home/.bbstrader/data/csv_data'
Source code in src/bbstrader/btengine/data.py
MT5DataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
List[str]
|
A list of symbol strings to download data for. |
required |
**kwargs
|
Any
|
Keyword arguments for data retrieval: time_frame (str): MT5 time frame (e.g., 'D1' for daily). mt5_start (datetime): Start date for historical data. mt5_end (datetime): End date for historical data. data_dir (str): Directory for storing data . |
{}
|
Note
Requires a working connection to an MT5 terminal.
See bbstrader.metatrader.rates.Rates for other arguments.
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
YFDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
yf_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
yf_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
EODHDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
eodhd_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
eodhd_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
eodhd_period
|
str
|
Time period for historical data (e.g., 'd', 'w', 'm', '1m', '5m', '1h'). |
required |
eodhd_api_key
|
str
|
API key for EOD Historical Data. |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
FMPDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
fmp_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
fmp_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
fmp_period
|
str
|
Time period for historical data (e.g. daily, weekly, monthly, quarterly, yearly, "1min", "5min", "15min", "30min", "1hour"). |
required |
fmp_api_key
|
str
|
API key for Financial Modeling Prep. |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
Event ¶
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.
MarketEvent ¶
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.
Initialises the MarketEvent.
Source code in src/bbstrader/btengine/event.py
SignalEvent ¶
SignalEvent(strategy_id: int, symbol: str, datetime: datetime, signal_type: Literal['LONG', 'SHORT', 'EXIT'], quantity: Union[int, float] = 100, strength: Union[int, float] = 1.0, price: Optional[Union[int, float]] = None, stoplimit: Optional[Union[int, float]] = None)
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.
Initialises the SignalEvent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
strategy_id
|
int
|
The unique identifier for the strategy that generated the signal. |
required |
symbol
|
str
|
The ticker symbol, e.g. 'GOOG'. |
required |
datetime
|
datetime
|
The timestamp at which the signal was generated. |
required |
signal_type
|
str
|
'LONG' or 'SHORT' or 'EXIT'. |
required |
quantity
|
int | float
|
An optional integer (or float) representing the order size. |
100
|
strength
|
int | float
|
An adjustment factor "suggestion" used to scale quantity at the portfolio level. Useful for pairs strategies. |
1.0
|
price
|
int | float
|
An optional price to be used when the signal is generated. |
None
|
stoplimit
|
int | float
|
An optional stop-limit price for the signal |
None
|
Source code in src/bbstrader/btengine/event.py
OrderEvent ¶
OrderEvent(symbol: str, order_type: Literal['MKT', 'LMT', 'STP', 'STPLMT'], quantity: Union[int, float], direction: Literal['BUY', 'SELL'], price: Optional[Union[int, float]] = None, signal: Optional[str] = None)
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.
Initialises the order type, setting whether it is a Market order ('MKT') or Limit order ('LMT'), or Stop order ('STP'). a quantity (integral or float) and its direction ('BUY' or 'SELL').
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
The instrument to trade. |
required |
order_type
|
str
|
'MKT' or 'LMT' for Market or Limit. |
required |
quantity
|
int | float
|
Non-negative number for quantity. |
required |
direction
|
str
|
'BUY' or 'SELL' for long or short. |
required |
price
|
int | float
|
The price at which to order. |
None
|
signal
|
str
|
The signal that generated the order. |
None
|
Source code in src/bbstrader/btengine/event.py
print_order ¶
Outputs the values within the Order.
Source code in src/bbstrader/btengine/event.py
FillEvent ¶
FillEvent(timeindex: datetime, symbol: str, exchange: str, quantity: Union[int, float], direction: Literal['BUY', 'SELL'], fill_cost: Optional[Union[int, float]], commission: Optional[float] = None, order: Optional[str] = None)
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.
Initialises the FillEvent object. Sets the symbol, exchange, quantity, direction, cost of fill and an optional commission.
If commission is not provided, the Fill object will calculate it based on the trade size and Interactive Brokers fees.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeindex
|
datetime
|
The bar-resolution when the order was filled. |
required |
symbol
|
str
|
The instrument which was filled. |
required |
exchange
|
str
|
The exchange where the order was filled. |
required |
quantity
|
int | float
|
The filled quantity. |
required |
direction
|
str
|
The direction of fill |
required |
fill_cost
|
int | float
|
Price of the shares when filled. |
required |
commission
|
float | None
|
An optional commission sent from IB. |
None
|
order
|
str
|
The order that this fill is related |
None
|
Source code in src/bbstrader/btengine/event.py
calculate_ib_commission ¶
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
Source code in src/bbstrader/btengine/event.py
ExecutionHandler ¶
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.
execute_order
abstractmethod
¶
Takes an Order event and executes it, producing a Fill event that gets placed onto the Events queue.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
SimExecutionHandler ¶
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.
Initialises the handler, setting the event queues up internally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Queue of Event objects. |
required |
Source code in src/bbstrader/btengine/execution.py
process_pending ¶
Fill orders held under time-frontier mode at the current (next) bar.
Called by the engine once per bar after new data arrives. Orders placed
on the previous bar fill here at this bar's fill_on price.
Source code in src/bbstrader/btengine/execution.py
execute_order ¶
Converts Order objects into Fill objects, optionally applying the configured slippage, market-impact, commission, partial-fill and time-frontier (next-bar) models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
MT5ExecutionHandler ¶
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
for indicies, forex, commodities and crypto. We used the Admiral Market
account fee calculation model from Trade.MT5 account type for stocks and ETFs.
NOTE
This class only works with bbstrader.metatrader.data.MT5DataHandler class.
Initialises the handler, setting the event queues up internally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Queue of Event objects. |
required |
Source code in src/bbstrader/btengine/execution.py
execute_order ¶
Executes an Order event by converting it into a Fill event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
ExperimentRecord
dataclass
¶
ExperimentRecord(id: str, name: str, params: Dict[str, Any], metrics: Dict[str, Any], created_at: str, environment: Dict[str, str] = dict())
A persisted record of one backtest/optimization run.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
The unique run identifier. |
name |
str
|
The human-readable run name. |
params |
Dict[str, Any]
|
The parameters the run was executed with. |
metrics |
Dict[str, Any]
|
The metrics produced by the run. |
created_at |
str
|
The ISO-8601 UTC creation timestamp. |
environment |
Dict[str, str]
|
The Python/platform environment captured at save time. |
to_dict ¶
Return the record as a plain dict suitable for JSON serialization.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict[str, Any]: All fields of the record. |
ExperimentStore ¶
Save, load, list and compare persisted experiment runs.
Initialise the store rooted at root and ensure it exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Optional[Union[str, Path]]
|
Directory to store runs in.
Defaults to |
None
|
Source code in src/bbstrader/btengine/experiment.py
save ¶
save(name: str, params: Dict[str, Any], metrics: Dict[str, Any], equity_curve: Optional[DataFrame] = None, run_id: Optional[str] = None, created_at: Optional[str] = None) -> str
Persist a run and return its id.
run_id/created_at may be supplied for deterministic, idempotent
writes (e.g. in tests); otherwise a uuid and the current UTC time are
used.
Source code in src/bbstrader/btengine/experiment.py
load ¶
Load a previously saved run's metadata record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier returned by :meth: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ExperimentRecord |
ExperimentRecord
|
The reconstructed record. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If no run with |
Source code in src/bbstrader/btengine/experiment.py
load_equity ¶
Load a run's persisted equity curve, if one was saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier. |
required |
Returns:
| Type | Description |
|---|---|
Optional[DataFrame]
|
Optional[pd.DataFrame]: The equity curve, or None when absent. |
Source code in src/bbstrader/btengine/experiment.py
list ¶
List all saved runs, oldest first.
Returns:
| Type | Description |
|---|---|
List[ExperimentRecord]
|
List[ExperimentRecord]: Records sorted by creation time. |
Source code in src/bbstrader/btengine/experiment.py
compare ¶
Return a leaderboard DataFrame of all runs' metrics.
Sorted by metric (descending by default) when provided.
Source code in src/bbstrader/btengine/experiment.py
delete ¶
Delete a saved run and all of its files.
A no-op when the run does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier to delete. |
required |
Source code in src/bbstrader/btengine/experiment.py
SlippageModel ¶
Bases: ABC
Adjusts the execution price to account for adverse price movement.
adjusted_price
abstractmethod
¶
adjusted_price(base_price: float, direction: str, quantity: float, symbol: str, bardata: DataHandler) -> float
Return the slippage-adjusted execution price.
NoSlippage ¶
Bases: SlippageModel
Fills at the unadjusted base price.
adjusted_price ¶
Return base_price unchanged (see :meth:SlippageModel.adjusted_price).
FixedSpreadSlippage ¶
Bases: SlippageModel
Charges half of a fixed spread (in price units) on each fill.
Initialise the model with a fixed spread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spread
|
float
|
The full bid-ask spread in price units; half is charged on each fill. Must be non-negative. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted adversely by half the fixed spread.
PercentSlippage ¶
Bases: SlippageModel
Applies a fixed percentage slippage to the base price.
Initialise the model with a fractional slippage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
The slippage as a fraction of price (for example
|
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price moved adversely by the configured percentage.
VolatilitySlippage ¶
Bases: SlippageModel
Slippage scaled by recent return volatility.
slippage = coef * sigma * base_price where sigma is the rolling
standard deviation of returns over window bars.
Initialise the model with a volatility coefficient and window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Multiplier applied to the rolling return standard deviation to size the slippage. |
1.0
|
window
|
int
|
Number of recent bars used to estimate volatility. |
20
|
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted by coef * sigma of recent returns.
Source code in src/bbstrader/btengine/friction.py
VolumeParticipationSlippage ¶
Bases: SlippageModel
Slippage proportional to the order's share of bar volume.
Initialise the model with a participation coefficient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Multiplier applied to the order's share of bar volume to size the slippage. |
0.1
|
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted by the order's share of bar volume.
Source code in src/bbstrader/btengine/friction.py
NoImpact ¶
Bases: MarketImpactModel
No market impact.
SquareRootImpact ¶
Bases: MarketImpactModel
The square-root impact model: impact proportional to sqrt(size / ADV).
impact = coef * base_price * sqrt(|quantity| / adv). Suitable for
institution-scale sizing where impact grows sub-linearly with order size.
Initialise the model with an impact coefficient and ADV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Scales the impact; larger values model thinner books. |
0.1
|
adv
|
float
|
Average daily volume used to normalise order size. Must be positive. |
1000000.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
impact ¶
Return the adverse per-unit impact coef * price * sqrt(|qty|/adv).
Source code in src/bbstrader/btengine/friction.py
ZeroCommission ¶
Bases: CommissionModel
No commission.
FixedCommission ¶
Bases: CommissionModel
A flat fee per fill.
Initialise the model with the flat per-fill fee.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
float
|
The fee charged on every fill, in account currency. |
required |
Source code in src/bbstrader/btengine/friction.py
commission ¶
PerShareCommission ¶
Bases: CommissionModel
A per-share/contract fee with an optional minimum.
Initialise the model with a per-share rate and floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_share
|
float
|
Fee charged per share or contract filled. |
0.005
|
minimum
|
float
|
Minimum commission applied to any fill. |
1.0
|
Source code in src/bbstrader/btengine/friction.py
commission ¶
PercentCommission ¶
Bases: CommissionModel
A commission as a percentage of notional with an optional minimum.
Initialise the model with a notional percentage and floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
Fraction of traded notional charged as commission. |
0.001
|
minimum
|
float
|
Minimum commission applied to any fill. |
0.0
|
Source code in src/bbstrader/btengine/friction.py
commission ¶
IBCommission ¶
Bases: CommissionModel
The Interactive Brokers tiered share commission used by FillEvent.
commission ¶
Return the IB tiered per-share commission for the fill.
FundingModel ¶
Bases: ABC
Charges the per-bar carrying cost of holding an open position.
Unlike slippage, impact and commission - which apply once at the fill - a funding model is evaluated every bar a position is held, capturing the overnight/swap financing that dominates the cost of leveraged CFD and FX positions. The returned value is a cash flow (positive = a cost debited from the account, negative = a credit) so a model can charge longs while crediting shorts, or vice versa.
carry
abstractmethod
¶
Return the per-bar carry cash flow for an open position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
The instrument the position is held in. |
required |
quantity
|
float
|
The signed position size; positive for a long, negative for a short. |
required |
price
|
float
|
The current mark-to-market price of one unit. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The cash flow for holding the position over one bar. A |
float
|
positive number is a cost debited from cash; a negative number is a |
|
float
|
credit added to cash. |
Source code in src/bbstrader/btengine/friction.py
NoFunding ¶
Bases: FundingModel
Applies no carrying cost; positions are free to hold.
carry ¶
Return zero carry regardless of the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; present for interface compatibility. |
required |
quantity
|
float
|
Unused; present for interface compatibility. |
required |
price
|
float
|
Unused; present for interface compatibility. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Always |
Source code in src/bbstrader/btengine/friction.py
FixedRateFunding ¶
Bases: FundingModel
A simple cost-of-carry charged as an annual rate on notional.
The per-bar cost is (annual_rate / periods) * quantity * price. Because
quantity is signed, a long position is debited and a short position is
credited at the same rate, mirroring a basic financing model where the
holder of a long leveraged position pays to borrow. Supply short_rate to
charge shorts at a different annual rate (for example a borrow fee that makes
shorting a net cost rather than a credit).
Initialise the model with annualised financing rates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
annual_rate
|
float
|
The annual financing rate applied to long
notional (for example |
required |
periods
|
int
|
The number of bars per year used to convert the
annual rate to a per-bar rate (for example |
252
|
short_rate
|
Optional[float]
|
The annual rate applied to short
notional. When |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
carry ¶
Return the per-bar financing cash flow for the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; the rate is instrument independent. |
required |
quantity
|
float
|
The signed position size. |
required |
price
|
float
|
The current mark-to-market price of one unit. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
|
float
|
for positive quantities and |
|
float
|
positive result is debited from cash. |
Source code in src/bbstrader/btengine/friction.py
BrokerSwapFunding ¶
Bases: FundingModel
Per-unit swap points charged each bar, mirroring MT5 swap semantics.
Brokers quote a long and a short swap per lot/unit; this model charges
points * |quantity| * point_value each bar, using the long or short
points according to the sign of the position. Points are expressed as a
cost: positive points are debited from cash and negative points (a positive
swap) are credited.
Initialise the model with the broker's long/short swap points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
long_points
|
float
|
The swap cost per unit per bar applied to long positions. Positive debits cash; negative credits it. |
required |
short_points
|
float
|
The swap cost per unit per bar applied to short
positions, with the same sign convention as |
required |
point_value
|
float
|
The cash value of one swap point per unit, used to convert points to account currency. |
1.0
|
Source code in src/bbstrader/btengine/friction.py
carry ¶
Return the per-bar swap cash flow for the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; swap points are supplied per model instance. |
required |
quantity
|
float
|
The signed position size. |
required |
price
|
float
|
Unused; swap is charged per unit, not on notional. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
|
float
|
long or short swap selected by the sign of |
|
float
|
result is debited from cash. |
Source code in src/bbstrader/btengine/friction.py
Portfolio ¶
Portfolio(bars: DataHandler, events: Queue[Union[OrderEvent, FillEvent, SignalEvent]], start_date: datetime, initial_capital: float = 100000.0, **kwargs: Any)
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.
Initialises the portfolio with bars and an event queue. Also includes a starting datetime index and initial capital (USD unless otherwise stated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bars
|
DataHandler
|
The DataHandler object with current market data. |
required |
events
|
Queue
|
The Event Queue object. |
required |
start_date
|
datetime
|
The start date (bar) of the portfolio. |
required |
initial_capital
|
float
|
The starting capital in USD. |
100000.0
|
kwargs
|
dict
|
Additional arguments
- |
{}
|
Source code in src/bbstrader/btengine/portfolio.py
last_holding
property
¶
The most recently recorded holdings row (mark-to-market equity).
construct_all_positions ¶
Constructs the positions list using the start_date to determine when the time index will begin.
Source code in src/bbstrader/btengine/portfolio.py
construct_all_holdings ¶
Constructs the holdings list using the start_date to determine when the time index will begin.
Source code in src/bbstrader/btengine/portfolio.py
construct_current_holdings ¶
This constructs the dictionary which will hold the instantaneous value of the portfolio across all symbols.
Source code in src/bbstrader/btengine/portfolio.py
update_timeindex ¶
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.
Source code in src/bbstrader/btengine/portfolio.py
update_positions_from_fill ¶
Takes a Fill object and updates the position matrix to reflect the new position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fill
|
FillEvent
|
The Fill object to update the positions with. |
required |
Source code in src/bbstrader/btengine/portfolio.py
update_holdings_from_fill ¶
Takes a Fill object and updates the holdings matrix to reflect the holdings value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fill
|
FillEvent
|
The Fill object to update the holdings with. |
required |
Source code in src/bbstrader/btengine/portfolio.py
update_fill ¶
Updates the portfolio current positions and holdings from a FillEvent.
Source code in src/bbstrader/btengine/portfolio.py
generate_order ¶
Turns a SignalEvent into an OrderEvent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
SignalEvent
|
The tuple containing Signal information. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
OrderEvent |
Optional[OrderEvent]
|
The OrderEvent to be executed. |
Source code in src/bbstrader/btengine/portfolio.py
update_signal ¶
Acts on a SignalEvent to generate new orders based on the portfolio logic.
Source code in src/bbstrader/btengine/portfolio.py
create_equity_curve_dataframe ¶
Creates a pandas DataFrame from the all_holdings list of dictionaries.
Source code in src/bbstrader/btengine/portfolio.py
output_summary_stats ¶
Creates a list of summary statistics for the portfolio.
Source code in src/bbstrader/btengine/portfolio.py
BacktestStrategy ¶
BacktestStrategy(events: Queue[Union[SignalEvent, FillEvent]], symbol_list: List[str], bars: DataHandler, **kwargs: Any)
Bases: BaseStrategy
Strategy implementation specifically for Backtesting. Handles internal state for orders, positions, trades, and cash. Simulates order execution and pending orders.
Initialize the BacktestStrategy object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
The event queue. |
required | |
symbol_list
|
The list of symbols for the strategy. |
required | |
bars
|
The data handler object. |
required | |
**kwargs
|
Additional keyword arguments for other classes (e.g, Portfolio, ExecutionHandler). - max_trades : The maximum number of trades allowed per symbol. - time_frame : The time frame for the strategy. - logger : The logger object for the strategy. |
required |
Source code in src/bbstrader/btengine/strategy.py
orders
property
¶
The pending orders per symbol, keyed by order type.
trades
property
¶
The executed trade counts per symbol, keyed by side.
positions
property
¶
The open position sizes per symbol, keyed by LONG/SHORT.
holdings
property
¶
The current mark-to-market holdings value per symbol.
get_update_from_portfolio ¶
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:
| Name | Type | Description | Default |
|---|---|---|---|
positions
|
The positions for the symbols in the strategy. |
required | |
holdings
|
The holdings for the symbols in the strategy. |
required |
Source code in src/bbstrader/btengine/strategy.py
update_trades_from_fill ¶
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.
Source code in src/bbstrader/btengine/strategy.py
get_asset_values ¶
get_asset_values(symbol_list: List[str], window: int, value_type: str = 'returns', array: bool = True, **kwargs) -> Optional[Dict[str, Union[np.typing.NDArray, pd.Series]]]
Return the last window values of value_type for each symbol.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
The symbols to fetch values for. |
required |
window
|
int
|
The number of most-recent bars required per symbol. |
required |
value_type
|
str
|
The bar field to read (for example |
'returns'
|
array
|
bool
|
When True return NumPy arrays (NaNs dropped); when False return pandas Series sliced from the bar DataFrame. |
True
|
kwargs
|
Unused; accepted for forward compatibility. |
{}
|
Returns:
| Type | Description |
|---|---|
Optional[Dict[str, Union[NDArray, Series]]]
|
Optional[Dict[str, Union[NDArray, pd.Series]]]: A mapping of symbol |
Optional[Dict[str, Union[NDArray, Series]]]
|
to its last |
Optional[Dict[str, Union[NDArray, Series]]]
|
|
Source code in src/bbstrader/btengine/strategy.py
calculate_signals
abstractmethod
¶
Compute trading signals for the current bar.
Subclasses implement their strategy logic here, placing orders via the
buy_mkt/sell_mkt/close_positions helpers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event for the current bar. |
required |
Source code in src/bbstrader/btengine/strategy.py
buy_mkt ¶
buy_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a long position
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_mkt ¶
sell_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a short position
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
close_positions ¶
close_positions(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Close a position or exit all positions
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_stop ¶
buy_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a stop price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_stop ¶
sell_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a stop price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_limit ¶
buy_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_limit ¶
sell_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_stop_limit ¶
buy_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a stop-limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_stop_limit ¶
sell_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a stop-limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
check_pending_orders ¶
Check for pending orders and handle them accordingly.
Source code in src/bbstrader/btengine/strategy.py
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | |
MultiStrategy ¶
Runs several strategies against one shared portfolio, cash account and clock.
The engine sees a single strategy; this adapter fans every engine callback
out to each child strategy. All children post signals to the same event
queue, so the shared Portfolio nets their positions and allocates one
pool of capital enabling cross-strategy capital-allocation and netting
tests that a single-strategy engine cannot express.
Children are typically scoped to disjoint symbol sets; when they overlap, positions net at the portfolio level and each child's trade counters track its own fills for symbols it trades.
Wrap one or more child strategies behind a single engine interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
strategies
|
List[BacktestStrategy]
|
The child strategies to run against the shared portfolio. The union of their symbols becomes this adapter's symbol set. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/strategy.py
calculate_signals ¶
Fan the market event out to every child strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event for the current bar. |
required |
Source code in src/bbstrader/btengine/strategy.py
check_pending_orders ¶
get_update_from_portfolio ¶
Push the latest portfolio positions and holdings to each child.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
positions
|
Dict[str, float]
|
Current position sizes per symbol. |
required |
holdings
|
Dict[str, float]
|
Current holdings value per symbol. |
required |
Source code in src/bbstrader/btengine/strategy.py
MultiTimeFrame ¶
Derive completed higher-timeframe bars from a base-timeframe DataHandler.
Use inside a strategy's calculate_signals to read slow-timeframe context
while executing on the fast base feed::
mtf = MultiTimeFrame(self.data)
daily_close = mtf.htf_value(symbol, "D1") # last *completed* daily close
Wrap a base-timeframe DataHandler for higher-timeframe access.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataHandler
|
The base-timeframe data feed to resample from. |
required |
lookback
|
int
|
Default number of base bars to pull when resampling. |
1000
|
Source code in src/bbstrader/btengine/timeframe.py
htf_bars ¶
htf_bars(symbol: str, rule: str, n: Optional[int] = None, lookback: Optional[int] = None, drop_partial: bool = True) -> pd.DataFrame
Return resampled HTF bars for symbol.
With drop_partial (default) the final, possibly still-forming bucket
is dropped so only completed HTF bars are visible preventing
look-ahead. n limits the result to the most recent n bars.
Source code in src/bbstrader/btengine/timeframe.py
htf_value ¶
htf_value(symbol: str, rule: str, val_type: str = 'close', lookback: Optional[int] = None, drop_partial: bool = True) -> Optional[float]
Latest completed HTF value for symbol (None if not enough data).
Source code in src/bbstrader/btengine/timeframe.py
VectorizedResult
dataclass
¶
VectorizedResult(equity: NDArray[float64], returns: NDArray[float64], position: NDArray[float64], trades: List[Tuple[int, int]], init_cash: float, periods: int)
Result of a vectorized backtest with lazily computed metrics.
total_return
property
¶
The total return over the run as a fraction of initial capital.
max_drawdown
property
¶
Largest peak-to-trough drawdown of the equity curve (as a fraction).
win_rate
property
¶
The fraction of trades whose equity rose between entry and exit.
to_frame ¶
Return the run as a DataFrame of position, returns and equity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
Optional[Index]
|
An optional index (for example the price series' DatetimeIndex) to label the rows. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Columns |
Source code in src/bbstrader/btengine/vectorized.py
summary ¶
Return a dict of the headline metrics for the run.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
|
dict
|
|
Source code in src/bbstrader/btengine/vectorized.py
SMACrossoverStrategy ¶
Bases: _TemplateBase
Trend following: go long when the fast SMA crosses above the slow SMA.
kwargs
fast (int, default 10): Fast SMA window. slow (int, default 30): Slow SMA window. quantity (int, default 100): Units per trade.
Initialise the SMA crossover with fast/slow windows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Enter long on an up-cross and exit on a down-cross of the SMAs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
RSIMeanReversionStrategy ¶
Bases: _TemplateBase
Mean reversion: buy when RSI is oversold, exit when it recovers.
kwargs
period (int, default 14): RSI lookback. oversold (float, default 30): Entry threshold. exit_level (float, default 55): Exit threshold. quantity (int, default 100): Units per trade.
Initialise the RSI mean-reversion thresholds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Buy when RSI is oversold and exit when it recovers above the level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
DonchianBreakoutStrategy ¶
Bases: _TemplateBase
Breakout: go long when price closes above the prior N-bar high.
The channel is taken from the previous bar to avoid look-ahead. Exit when price closes below the prior N-bar low.
kwargs
window (int, default 20): Donchian channel lookback. quantity (int, default 100): Units per trade.
Initialise the Donchian breakout channel lookback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Go long on a close above the prior N-bar high; exit below the low.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
historical_var ¶
Historical Value-at-Risk as a positive loss fraction at level.
Source code in src/bbstrader/btengine/analytics.py
parametric_var ¶
Gaussian (parametric) Value-at-Risk as a positive loss fraction.
Source code in src/bbstrader/btengine/analytics.py
historical_cvar ¶
Historical Conditional VaR (expected shortfall) beyond the VaR threshold.
Source code in src/bbstrader/btengine/analytics.py
parametric_cvar ¶
Gaussian Conditional VaR (expected shortfall).
Source code in src/bbstrader/btengine/analytics.py
monte_carlo_bootstrap ¶
monte_carlo_bootstrap(returns: ReturnsLike, n_sims: int = 1000, horizon: Optional[int] = None, seed: int = 0, quantiles=(0.05, 0.5, 0.95)) -> MonteCarloResult
Bootstrap the return series into equity-curve confidence bands.
Resamples the historical returns with replacement to build n_sims equity
paths over horizon steps, then reports per-step percentile bands and the
terminal-return distribution. Deterministic given seed.
Source code in src/bbstrader/btengine/analytics.py
cusum_change_points ¶
Detect mean-shift change points with a two-sided CUSUM filter.
threshold is in units of the series' standard deviation. Returns the
indices at which the cumulative sum breaches the threshold (and resets).
Source code in src/bbstrader/btengine/analytics.py
volatility_regimes ¶
Label each bar by its volatility regime (0 = lowest vol .. n_states-1).
A lightweight, dependency-free alternative to an HMM: rolling volatility is
bucketed into n_states quantile bins. Useful for conditional-performance
analysis (how a strategy behaves in calm vs. turbulent regimes).
Source code in src/bbstrader/btengine/analytics.py
factor_exposure ¶
factor_exposure(returns: ReturnsLike, factors: Union[DataFrame, Series, NDArray]) -> Dict[str, float]
OLS factor regression: alpha, factor betas and R-squared.
factors may be a single series (e.g. the market) or a DataFrame of
factor returns aligned to returns. Returns alpha, one beta per factor
and the regression R-squared.
Source code in src/bbstrader/btengine/analytics.py
rolling_beta ¶
Rolling market beta (cov/var) over window bars; NaN until warmed up.
Source code in src/bbstrader/btengine/analytics.py
run_backtest ¶
run_backtest(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], exc_handler: Optional[Type[ExecutionHandler]] = None, initial_capital: float = 100000.0, heartbeat: float = 0.0, **kwargs: Any) -> pd.DataFrame
Runs a backtest simulation based on a DataHandler, Strategy, and ExecutionHandler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
List of symbol strings for the assets to be backtested. |
required |
start_date
|
datetime
|
Start date of the backtest. |
required |
data_handler
|
DataHandler
|
A subclass of the |
required |
strategy
|
Strategy
|
The trading strategy to be employed during the backtest.
The strategy must be a subclass of Additional parameters specific to the strategy should be passed in |
required |
exc_handler
|
ExecutionHandler
|
The execution handler for managing order executions.
If not provided, a |
None
|
initial_capital
|
float
|
The initial capital for the portfolio in the backtest. Default is 100,000. |
100000.0
|
heartbeat
|
float
|
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 |
0.0
|
**kwargs
|
Any
|
Additional parameters passed to the |
{}
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: The portfolio values over time (capital, equities, returns etc.). |
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 examples.strategies import StockIndexSTBOTrading from bbstrader.config import config_logger from bbstrader.btengine.data import MT5DataHandler from bbstrader.btengine.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 ... )
Source code in src/bbstrader/btengine/backtest.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | |
has_pyarrow ¶
Return True if a Parquet engine (pyarrow) is importable.
expand_param_grid ¶
expand_param_grid(param_grid: Dict[str, Sequence[Any]], search: str = 'grid', n_iter: Optional[int] = None, seed: int = 0) -> List[Dict[str, Any]]
Expand a parameter grid into a list of concrete parameter dicts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
param_grid
|
Dict[str, Sequence[Any]]
|
Mapping of parameter name to the sequence of values to try. |
required |
search
|
str
|
|
'grid'
|
n_iter
|
Optional[int]
|
Number of combinations to sample when |
None
|
seed
|
int
|
Seed for the random sampler (deterministic by default). |
0
|
Source code in src/bbstrader/btengine/optimize.py
walk_forward ¶
walk_forward(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], param_grid: Dict[str, Sequence[Any]], exc_handler: Optional[Type[ExecutionHandler]] = None, initial_capital: float = 100000.0, metric: str = 'sharpe', periods: int = 252, n_splits: int = 3, anchored: bool = True, **kwargs: Any) -> pd.DataFrame
Anchored or rolling walk-forward validation.
The full history is divided into n_splits + 1 equal segments. For each
fold the in-sample window is optimized (in-process), and the best parameter
set is evaluated on the next out-of-sample segment. With anchored=True
the in-sample window always starts at bar 0 and grows; with anchored=False
it rolls forward at a fixed length.
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per fold: the chosen parameters plus the out-of-sample metrics. |
Source code in src/bbstrader/btengine/optimize.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | |
probabilistic_sharpe_ratio ¶
probabilistic_sharpe_ratio(sharpe: float, n_obs: int, benchmark: float = 0.0, skew: float = 0.0, kurtosis: float = 3.0) -> float
Probability that the true Sharpe exceeds benchmark (PSR).
sharpe and benchmark are per-observation (non-annualized) Sharpe
ratios. skew/kurtosis are the return distribution's moments
(kurtosis 3 == normal).
Source code in src/bbstrader/btengine/overfitting.py
expected_max_sharpe ¶
Expected maximum of n_trials independent Sharpe estimates.
The benchmark a strategy must beat to be considered non-random when it was
selected from n_trials candidates (Bailey & Lopez de Prado).
Source code in src/bbstrader/btengine/overfitting.py
deflated_sharpe_ratio ¶
deflated_sharpe_ratio(sharpe: float, n_obs: int, n_trials: int, sharpe_variance: float, skew: float = 0.0, kurtosis: float = 3.0) -> float
Deflated Sharpe Ratio (DSR).
PSR computed against the expected maximum Sharpe across n_trials, i.e.
the probability the strategy's Sharpe is real after accounting for multiple
testing. sharpe/sharpe_variance are per-observation.
Source code in src/bbstrader/btengine/overfitting.py
cscv_pbo ¶
cscv_pbo(performance: NDArray[float64], n_splits: int = 10, metric: Optional[Callable[[NDArray[float64]], float]] = None) -> float
Probability of Backtest Overfitting via combinatorially symmetric CV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
performance
|
NDArray[float64]
|
A (T, N) matrix of per-observation returns for N candidate configurations over T observations. |
required |
n_splits
|
int
|
Number of disjoint row blocks S (must be even); IS/OOS are all C(S, S/2) balanced partitions. |
10
|
metric
|
Optional[Callable[[NDArray[float64]], float]]
|
Per-configuration score from a sub-matrix of returns. Defaults to the Sharpe ratio. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
PBO in [0, 1]: the fraction of partitions where the in-sample best |
float
|
configuration ranks below the out-of-sample median. |
Source code in src/bbstrader/btengine/overfitting.py
combinatorial_splits ¶
combinatorial_splits(n_obs: int, n_groups: int = 6, n_test_groups: int = 2, embargo: int = 0) -> Iterator[Tuple[NDArray[np.int_], NDArray[np.int_]]]
Yield combinatorial purged cross-validation (CPCV) train/test splits.
Observations are partitioned into n_groups contiguous blocks; every
combination of n_test_groups blocks forms a test set, with the remaining
blocks (minus an embargo band around each test block, to prevent
leakage) as the training set. Yields C(n_groups, n_test_groups) folds.
Source code in src/bbstrader/btengine/overfitting.py
get_asset_performances ¶
get_asset_performances(portfolio: DataFrame, assets: List[str], plot: bool = True, strategy: str = '') -> pd.Series
Calculate the performance of the assets in the portfolio.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
portfolio
|
DataFrame
|
The portfolio DataFrame. |
required |
assets
|
List[str]
|
The list of assets to calculate the performance for. |
required |
plot
|
bool
|
Whether to plot the performance of the assets. |
True
|
strategy
|
str
|
The name of the strategy. |
''
|
Returns:
| Type | Description |
|---|---|
Series
|
pd.Series: The performance of the assets. |
Source code in src/bbstrader/btengine/performance.py
get_perfbased_weights ¶
Calculate the weights of the assets based on their performances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
performances
|
Series
|
The performances of the assets. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dict[str, float]: The weights of the assets. |
Source code in src/bbstrader/btengine/performance.py
create_sharpe_ratio ¶
Create the Sharpe ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
S |
float
|
Sharpe ratio |
Source code in src/bbstrader/btengine/performance.py
create_sortino_ratio ¶
Create the Sortino ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
S |
float
|
Sortino ratio |
Source code in src/bbstrader/btengine/performance.py
create_omega_ratio ¶
Create the Omega ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
rf
|
float
|
Risk-free rate. |
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Omega ratio |
Source code in src/bbstrader/btengine/performance.py
create_calmar_ratio ¶
Create the Calmar ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Calmar ratio |
Source code in src/bbstrader/btengine/performance.py
create_tail_ratio ¶
Create the Tail ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Tail ratio |
Source code in src/bbstrader/btengine/performance.py
calculate_risk_metrics ¶
calculate_risk_metrics(returns: Series, benchmark_returns: Series, periods: int = 252) -> Dict[str, float]
Calculate Alpha, Beta and Volatility for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
benchmark_returns
|
A pandas Series representing benchmark period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dict[str, float]: Alpha, Beta, Volatility |
Source code in src/bbstrader/btengine/performance.py
create_drawdowns ¶
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:
| Name | Type | Description | Default |
|---|---|---|---|
pnl
|
A pandas Series representing period percentage returns. |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
drawdown, duration - high-water mark, duration. |
Source code in src/bbstrader/btengine/performance.py
plot_performance ¶
Plot the performance of the strategy
- (Portfolio value, %)
- (Period returns, %)
- (Drawdowns, %)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
|
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
plot_returns_and_dd ¶
Plot the returns and drawdowns of the strategy compared to a benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The DataFrame containing the strategy returns and drawdowns. |
required |
benchmark
|
str
|
The ticker symbol of the benchmark to compare the strategy to. |
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
plot_monthly_yearly_returns ¶
Plot the monthly and yearly returns of the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
|
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | |
show_qs_stats ¶
show_qs_stats(returns: Series, benchmark: str, strategy_name: str, save_dir: Optional[str] = None) -> None
Generate the full quantstats report for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Serie
|
The DataFrame containing the strategy returns and drawdowns. |
required |
benchmark
|
str
|
The ticker symbol of the benchmark to compare the strategy to. |
required |
strategy_name
|
str
|
The name of the strategy. |
required |
Source code in src/bbstrader/btengine/performance.py
resample_ohlcv ¶
resample_ohlcv(df: DataFrame, rule: str, *, label: str = 'left', closed: str = 'left') -> pd.DataFrame
Aggregate an OHLCV DataFrame up to a higher timeframe.
open=first, high=max, low=min, close=last, volume=sum (adj_close=last when
present). Buckets with no data are dropped. df must have a DatetimeIndex.
Source code in src/bbstrader/btengine/timeframe.py
vectorized_backtest ¶
vectorized_backtest(close: ArrayLike, entries: ArrayLike, exits: ArrayLike, *, short_entries: Optional[ArrayLike] = None, short_exits: Optional[ArrayLike] = None, allow_short: bool = False, init_cash: float = 100000.0, fees: float = 0.0, slippage: float = 0.0, periods: int = 252) -> VectorizedResult
Run a fully vectorized signal backtest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
close
|
ArrayLike
|
Price series. |
required |
entries
|
ArrayLike
|
Boolean array; True opens a long position. |
required |
exits
|
ArrayLike
|
Boolean array; True closes the long position. |
required |
short_entries / short_exits
|
Optional short-side signals (require
|
required | |
allow_short
|
bool
|
Permit short positions. |
False
|
init_cash
|
float
|
Starting capital. |
100000.0
|
fees
|
float
|
Per-unit-turnover fee as a fraction of notional (e.g. 0.0005). |
0.0
|
slippage
|
float
|
Per-unit-turnover slippage as a fraction of notional. |
0.0
|
periods
|
int
|
Annualization factor for the Sharpe ratio. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
VectorizedResult
|
class: |
Source code in src/bbstrader/btengine/vectorized.py
analytics ¶
Institutional risk analytics: VaR/CVaR, Monte Carlo, regimes, factor exposure.
These functions extend the quantstats-backed :mod:bbstrader.btengine.performance
metrics with ex-ante risk and attribution tools. They operate on plain return
series (NumPy arrays or pandas Series) and are deterministic the Monte Carlo
routines take an explicit seed so they are safe for reproducible research.
MonteCarloResult
dataclass
¶
MonteCarloResult(terminal_returns: NDArray[float64], bands: Dict[str, NDArray[float64]], horizon: int)
Monte Carlo simulated terminal-return distribution and equity bands.
quantile ¶
Return the q-quantile of the simulated terminal returns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
float
|
Quantile in the interval [0, 1]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The terminal return at quantile |
Source code in src/bbstrader/btengine/analytics.py
historical_var ¶
Historical Value-at-Risk as a positive loss fraction at level.
Source code in src/bbstrader/btengine/analytics.py
parametric_var ¶
Gaussian (parametric) Value-at-Risk as a positive loss fraction.
Source code in src/bbstrader/btengine/analytics.py
historical_cvar ¶
Historical Conditional VaR (expected shortfall) beyond the VaR threshold.
Source code in src/bbstrader/btengine/analytics.py
parametric_cvar ¶
Gaussian Conditional VaR (expected shortfall).
Source code in src/bbstrader/btengine/analytics.py
monte_carlo_bootstrap ¶
monte_carlo_bootstrap(returns: ReturnsLike, n_sims: int = 1000, horizon: Optional[int] = None, seed: int = 0, quantiles=(0.05, 0.5, 0.95)) -> MonteCarloResult
Bootstrap the return series into equity-curve confidence bands.
Resamples the historical returns with replacement to build n_sims equity
paths over horizon steps, then reports per-step percentile bands and the
terminal-return distribution. Deterministic given seed.
Source code in src/bbstrader/btengine/analytics.py
cusum_change_points ¶
Detect mean-shift change points with a two-sided CUSUM filter.
threshold is in units of the series' standard deviation. Returns the
indices at which the cumulative sum breaches the threshold (and resets).
Source code in src/bbstrader/btengine/analytics.py
volatility_regimes ¶
Label each bar by its volatility regime (0 = lowest vol .. n_states-1).
A lightweight, dependency-free alternative to an HMM: rolling volatility is
bucketed into n_states quantile bins. Useful for conditional-performance
analysis (how a strategy behaves in calm vs. turbulent regimes).
Source code in src/bbstrader/btengine/analytics.py
factor_exposure ¶
factor_exposure(returns: ReturnsLike, factors: Union[DataFrame, Series, NDArray]) -> Dict[str, float]
OLS factor regression: alpha, factor betas and R-squared.
factors may be a single series (e.g. the market) or a DataFrame of
factor returns aligned to returns. Returns alpha, one beta per factor
and the regression R-squared.
Source code in src/bbstrader/btengine/analytics.py
rolling_beta ¶
Rolling market beta (cov/var) over window bars; NaN until warmed up.
Source code in src/bbstrader/btengine/analytics.py
backtest ¶
BacktestEngine ¶
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)
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.
Initialises the backtest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
The list of symbol strings. |
required |
intial_capital
|
float
|
The starting capital for the portfolio. |
required |
heartbeat
|
float
|
Backtest "heartbeat" in seconds |
required |
start_date
|
datetime
|
The start datetime of the strategy. |
required |
data_handler (DataHandler)
|
Handles the market data feed. |
required | |
execution_handler (ExecutionHandler)
|
Handles the orders/fills for trades. |
required | |
strategy
|
Strategy
|
Generates signals based on market data. |
required |
kwargs
|
Additional parameters based on the |
required |
Source code in src/bbstrader/btengine/backtest.py
simulate_trading ¶
Simulates the backtest and outputs portfolio performance.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: The portfilio values over time (capital, equity, returns etc.) |
Source code in src/bbstrader/btengine/backtest.py
run_backtest ¶
run_backtest(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], exc_handler: Optional[Type[ExecutionHandler]] = None, initial_capital: float = 100000.0, heartbeat: float = 0.0, **kwargs: Any) -> pd.DataFrame
Runs a backtest simulation based on a DataHandler, Strategy, and ExecutionHandler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
List of symbol strings for the assets to be backtested. |
required |
start_date
|
datetime
|
Start date of the backtest. |
required |
data_handler
|
DataHandler
|
A subclass of the |
required |
strategy
|
Strategy
|
The trading strategy to be employed during the backtest.
The strategy must be a subclass of Additional parameters specific to the strategy should be passed in |
required |
exc_handler
|
ExecutionHandler
|
The execution handler for managing order executions.
If not provided, a |
None
|
initial_capital
|
float
|
The initial capital for the portfolio in the backtest. Default is 100,000. |
100000.0
|
heartbeat
|
float
|
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 |
0.0
|
**kwargs
|
Any
|
Additional parameters passed to the |
{}
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: The portfolio values over time (capital, equities, returns etc.). |
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 examples.strategies import StockIndexSTBOTrading from bbstrader.config import config_logger from bbstrader.btengine.data import MT5DataHandler from bbstrader.btengine.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 ... )
Source code in src/bbstrader/btengine/backtest.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | |
run_backtest_with ¶
run_backtest_with(engine: Literal['bbstrader', 'cerebro', 'zipline'], **kwargs: Any) -> Optional[pd.DataFrame]
Source code in src/bbstrader/btengine/backtest.py
catalog ¶
A unified, cached data catalog over the existing data handlers.
Today the download handlers re-fetch on every run. The catalog adds a real
local store with cache-hit semantics and point-in-time metadata, so re-runs are
instant and offline-capable. Data is persisted as Parquet when pyarrow
is available and transparently falls back to CSV otherwise, so a lean
install (without the [catalog] extra) keeps working.
The store is intentionally decoupled from the handler plumbing: fetch takes
any zero-argument loader that returns a normalized OHLCV DataFrame, which
makes it trivial to back with YFDataHandler, a broker API, or a test stub.
DataCatalog ¶
A local OHLCV cache keyed by (source, symbol, timeframe).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_dir
|
Optional[str]
|
Root directory for the store. Defaults to
|
None
|
fmt
|
str
|
|
'auto'
|
Initialise the catalog and ensure its base directory exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_dir
|
Optional[str]
|
Root directory for the store. Defaults to
|
None
|
fmt
|
str
|
One of |
'auto'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/catalog.py
has ¶
metadata ¶
Return the stored metadata for the key, or None if absent.
Source code in src/bbstrader/btengine/catalog.py
is_fresh ¶
Return True if the cached dataset exists and is within max_age_days.
A max_age_days of None means "never expires" (any cached copy is
fresh); a value of 0 (or negative) means the cache is always stale,
independent of clock resolution.
Source code in src/bbstrader/btengine/catalog.py
get ¶
Load a cached dataset, or None if it is not present.
Source code in src/bbstrader/btengine/catalog.py
put ¶
put(df: DataFrame, source: str, symbol: str, timeframe: str, extra_meta: Optional[Dict[str, Any]] = None) -> Path
Persist df for the key and write a metadata sidecar.
Source code in src/bbstrader/btengine/catalog.py
fetch ¶
fetch(loader: Callable[[], DataFrame], source: str, symbol: str, timeframe: str = 'D1', max_age_days: Optional[float] = None, force: bool = False, extra_meta: Optional[Dict[str, Any]] = None) -> pd.DataFrame
Return cached data if fresh, otherwise call loader and cache it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loader
|
Callable[[], DataFrame]
|
Zero-argument callable returning a normalized OHLCV DataFrame
(only called on a cache miss or when |
required |
source
|
str
|
Logical source name (e.g. |
required |
symbol
|
str
|
Instrument symbol. |
required |
timeframe
|
str
|
Bar timeframe/period label used in the cache key. |
'D1'
|
max_age_days
|
Optional[float]
|
Maximum acceptable cache age; None means never expires. |
None
|
force
|
bool
|
Bypass the cache and always reload. |
False
|
Source code in src/bbstrader/btengine/catalog.py
list_datasets ¶
Return metadata for every dataset currently in the store.
Source code in src/bbstrader/btengine/catalog.py
has_pyarrow ¶
Return True if a Parquet engine (pyarrow) is importable.
data ¶
DataHandler ¶
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.
get_latest_bar
abstractmethod
¶
get_latest_bars
abstractmethod
¶
Returns the last N bars updated.
Source code in src/bbstrader/btengine/data.py
get_latest_bar_datetime
abstractmethod
¶
Returns a Python datetime object for the last bar.
get_latest_bar_value
abstractmethod
¶
Returns one of the Open, High, Low, Close, Adj Close, Volume or Returns from the last bar.
Source code in src/bbstrader/btengine/data.py
get_latest_bars_values
abstractmethod
¶
Returns the last N bar values from the latest_symbol list, or N-k if less available.
Source code in src/bbstrader/btengine/data.py
update_bars
abstractmethod
¶
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).
Source code in src/bbstrader/btengine/data.py
BaseCSVDataHandler ¶
BaseCSVDataHandler(events: Queue[MarketEvent], symbol_list: List[str], csv_dir: str, columns: Optional[List[str]] = None, index_col: Union[str, int, List[str], List[int]] = 0, persist_normalized: bool = True)
Bases: DataHandler
Base class for handling data loaded from CSV files.
Initialises the data handler by requesting the location of the CSV files and a list of symbols.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
The Event Queue. |
required | |
symbol_list
|
A list of symbol strings. |
required | |
csv_dir
|
Absolute directory path to the CSV files. |
required | |
columns
|
List of column names to use for the data. |
required | |
index_col
|
Column to use as the index. |
required | |
persist_normalized
|
Whether to write the normalized frame back to
|
required |
Source code in src/bbstrader/btengine/data.py
reset ¶
Rewinds the handler to the start so the same data can be replayed.
Enables parameter sweeps, walk-forward folds, and Monte Carlo passes without reconstructing the handler.
Source code in src/bbstrader/btengine/data.py
get_latest_bar ¶
Returns the last bar from the latest_symbol list.
Source code in src/bbstrader/btengine/data.py
get_latest_bars ¶
Returns the last N bars from the latest_symbol list, or N-k if less available.
Source code in src/bbstrader/btengine/data.py
get_latest_bar_datetime ¶
Returns a Python datetime object for the last bar.
Source code in src/bbstrader/btengine/data.py
get_latest_bars_datetime ¶
Returns a list of Python datetime objects for the last N bars.
Source code in src/bbstrader/btengine/data.py
get_latest_bar_value ¶
Returns one of the Open, High, Low, Close, Volume or OI values from the pandas Bar series object.
Source code in src/bbstrader/btengine/data.py
get_latest_bars_values ¶
Returns the last N bar values from the latest_symbol list, or N-k if less available.
Source code in src/bbstrader/btengine/data.py
update_bars ¶
Pushes the latest bar to the latest_symbol_data structure for all symbols in the symbol list.
Source code in src/bbstrader/btengine/data.py
CSVDataHandler ¶
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() .
Initialises the historic data handler by requesting
the location of the CSV files and a list of symbols.
It will be assumed that all files are of the form
symbol.csv, where symbol is a string in the list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue. |
required |
symbol_list
|
List[str]
|
A list of symbol strings. |
required |
csv_dir
|
str
|
Absolute directory path to the CSV files. |
required |
NOTE: All csv fille can be stored in 'Home/.bbstrader/data/csv_data'
Source code in src/bbstrader/btengine/data.py
MT5DataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
List[str]
|
A list of symbol strings to download data for. |
required |
**kwargs
|
Any
|
Keyword arguments for data retrieval: time_frame (str): MT5 time frame (e.g., 'D1' for daily). mt5_start (datetime): Start date for historical data. mt5_end (datetime): End date for historical data. data_dir (str): Directory for storing data . |
{}
|
Note
Requires a working connection to an MT5 terminal.
See bbstrader.metatrader.rates.Rates for other arguments.
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
YFDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
yf_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
yf_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
EODHDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
eodhd_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
eodhd_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
eodhd_period
|
str
|
Time period for historical data (e.g., 'd', 'w', 'm', '1m', '5m', '1h'). |
required |
eodhd_api_key
|
str
|
API key for EOD Historical Data. |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
FMPDataHandler ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Event Queue for passing market events. |
required |
symbol_list
|
list[str]
|
List of symbols to download data for. |
required |
fmp_start
|
str
|
Start date for historical data (YYYY-MM-DD). |
required |
fmp_end
|
str
|
End date for historical data (YYYY-MM-DD). |
required |
data_dir
|
str
|
Directory for caching data . |
required |
fmp_period
|
str
|
Time period for historical data (e.g. daily, weekly, monthly, quarterly, yearly, "1min", "5min", "15min", "30min", "1hour"). |
required |
fmp_api_key
|
str
|
API key for Financial Modeling Prep. |
required |
Note
See bbstrader.btengine.data.BaseCSVDataHandler for other arguments.
Source code in src/bbstrader/btengine/data.py
event ¶
Event ¶
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.
MarketEvent ¶
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.
Initialises the MarketEvent.
Source code in src/bbstrader/btengine/event.py
SignalEvent ¶
SignalEvent(strategy_id: int, symbol: str, datetime: datetime, signal_type: Literal['LONG', 'SHORT', 'EXIT'], quantity: Union[int, float] = 100, strength: Union[int, float] = 1.0, price: Optional[Union[int, float]] = None, stoplimit: Optional[Union[int, float]] = None)
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.
Initialises the SignalEvent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
strategy_id
|
int
|
The unique identifier for the strategy that generated the signal. |
required |
symbol
|
str
|
The ticker symbol, e.g. 'GOOG'. |
required |
datetime
|
datetime
|
The timestamp at which the signal was generated. |
required |
signal_type
|
str
|
'LONG' or 'SHORT' or 'EXIT'. |
required |
quantity
|
int | float
|
An optional integer (or float) representing the order size. |
100
|
strength
|
int | float
|
An adjustment factor "suggestion" used to scale quantity at the portfolio level. Useful for pairs strategies. |
1.0
|
price
|
int | float
|
An optional price to be used when the signal is generated. |
None
|
stoplimit
|
int | float
|
An optional stop-limit price for the signal |
None
|
Source code in src/bbstrader/btengine/event.py
OrderEvent ¶
OrderEvent(symbol: str, order_type: Literal['MKT', 'LMT', 'STP', 'STPLMT'], quantity: Union[int, float], direction: Literal['BUY', 'SELL'], price: Optional[Union[int, float]] = None, signal: Optional[str] = None)
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.
Initialises the order type, setting whether it is a Market order ('MKT') or Limit order ('LMT'), or Stop order ('STP'). a quantity (integral or float) and its direction ('BUY' or 'SELL').
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
The instrument to trade. |
required |
order_type
|
str
|
'MKT' or 'LMT' for Market or Limit. |
required |
quantity
|
int | float
|
Non-negative number for quantity. |
required |
direction
|
str
|
'BUY' or 'SELL' for long or short. |
required |
price
|
int | float
|
The price at which to order. |
None
|
signal
|
str
|
The signal that generated the order. |
None
|
Source code in src/bbstrader/btengine/event.py
print_order ¶
Outputs the values within the Order.
Source code in src/bbstrader/btengine/event.py
FillEvent ¶
FillEvent(timeindex: datetime, symbol: str, exchange: str, quantity: Union[int, float], direction: Literal['BUY', 'SELL'], fill_cost: Optional[Union[int, float]], commission: Optional[float] = None, order: Optional[str] = None)
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.
Initialises the FillEvent object. Sets the symbol, exchange, quantity, direction, cost of fill and an optional commission.
If commission is not provided, the Fill object will calculate it based on the trade size and Interactive Brokers fees.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeindex
|
datetime
|
The bar-resolution when the order was filled. |
required |
symbol
|
str
|
The instrument which was filled. |
required |
exchange
|
str
|
The exchange where the order was filled. |
required |
quantity
|
int | float
|
The filled quantity. |
required |
direction
|
str
|
The direction of fill |
required |
fill_cost
|
int | float
|
Price of the shares when filled. |
required |
commission
|
float | None
|
An optional commission sent from IB. |
None
|
order
|
str
|
The order that this fill is related |
None
|
Source code in src/bbstrader/btengine/event.py
calculate_ib_commission ¶
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
Source code in src/bbstrader/btengine/event.py
execution ¶
ExecutionHandler ¶
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.
execute_order
abstractmethod
¶
Takes an Order event and executes it, producing a Fill event that gets placed onto the Events queue.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
SimExecutionHandler ¶
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.
Initialises the handler, setting the event queues up internally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Queue of Event objects. |
required |
Source code in src/bbstrader/btengine/execution.py
process_pending ¶
Fill orders held under time-frontier mode at the current (next) bar.
Called by the engine once per bar after new data arrives. Orders placed
on the previous bar fill here at this bar's fill_on price.
Source code in src/bbstrader/btengine/execution.py
execute_order ¶
Converts Order objects into Fill objects, optionally applying the configured slippage, market-impact, commission, partial-fill and time-frontier (next-bar) models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
MT5ExecutionHandler ¶
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
for indicies, forex, commodities and crypto. We used the Admiral Market
account fee calculation model from Trade.MT5 account type for stocks and ETFs.
NOTE
This class only works with bbstrader.metatrader.data.MT5DataHandler class.
Initialises the handler, setting the event queues up internally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Queue
|
The Queue of Event objects. |
required |
Source code in src/bbstrader/btengine/execution.py
execute_order ¶
Executes an Order event by converting it into a Fill event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
OrderEvent
|
Contains an Event object with order information. |
required |
Source code in src/bbstrader/btengine/execution.py
experiment ¶
A lightweight experiment/results store for reproducible research.
Persists each backtest/optimization run its parameters, metrics, equity
curve and environment to disk so runs can be reloaded, compared
leaderboard-style, and reproduced later. Metadata is JSON; the equity curve is
CSV. Defaults to ~/.bbstrader/experiments but any root works.
ExperimentRecord
dataclass
¶
ExperimentRecord(id: str, name: str, params: Dict[str, Any], metrics: Dict[str, Any], created_at: str, environment: Dict[str, str] = dict())
A persisted record of one backtest/optimization run.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
The unique run identifier. |
name |
str
|
The human-readable run name. |
params |
Dict[str, Any]
|
The parameters the run was executed with. |
metrics |
Dict[str, Any]
|
The metrics produced by the run. |
created_at |
str
|
The ISO-8601 UTC creation timestamp. |
environment |
Dict[str, str]
|
The Python/platform environment captured at save time. |
to_dict ¶
Return the record as a plain dict suitable for JSON serialization.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict[str, Any]: All fields of the record. |
ExperimentStore ¶
Save, load, list and compare persisted experiment runs.
Initialise the store rooted at root and ensure it exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Optional[Union[str, Path]]
|
Directory to store runs in.
Defaults to |
None
|
Source code in src/bbstrader/btengine/experiment.py
save ¶
save(name: str, params: Dict[str, Any], metrics: Dict[str, Any], equity_curve: Optional[DataFrame] = None, run_id: Optional[str] = None, created_at: Optional[str] = None) -> str
Persist a run and return its id.
run_id/created_at may be supplied for deterministic, idempotent
writes (e.g. in tests); otherwise a uuid and the current UTC time are
used.
Source code in src/bbstrader/btengine/experiment.py
load ¶
Load a previously saved run's metadata record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier returned by :meth: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ExperimentRecord |
ExperimentRecord
|
The reconstructed record. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If no run with |
Source code in src/bbstrader/btengine/experiment.py
load_equity ¶
Load a run's persisted equity curve, if one was saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier. |
required |
Returns:
| Type | Description |
|---|---|
Optional[DataFrame]
|
Optional[pd.DataFrame]: The equity curve, or None when absent. |
Source code in src/bbstrader/btengine/experiment.py
list ¶
List all saved runs, oldest first.
Returns:
| Type | Description |
|---|---|
List[ExperimentRecord]
|
List[ExperimentRecord]: Records sorted by creation time. |
Source code in src/bbstrader/btengine/experiment.py
compare ¶
Return a leaderboard DataFrame of all runs' metrics.
Sorted by metric (descending by default) when provided.
Source code in src/bbstrader/btengine/experiment.py
delete ¶
Delete a saved run and all of its files.
A no-op when the run does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str
|
The run identifier to delete. |
required |
Source code in src/bbstrader/btengine/experiment.py
friction ¶
Pluggable execution-friction models for the simulated backtester.
The default SimExecutionHandler fills instantly at the bar price with no
trading costs, which optimistically biases results. These models add realistic
friction slippage, market impact, commission, and partial fills so a
backtest survives the jump to live trading. They are all opt-in: a handler
constructed without friction models behaves exactly as before.
All models are plain, deterministic functions of the order and recent bar data, so backtests stay reproducible.
SlippageModel ¶
Bases: ABC
Adjusts the execution price to account for adverse price movement.
adjusted_price
abstractmethod
¶
adjusted_price(base_price: float, direction: str, quantity: float, symbol: str, bardata: DataHandler) -> float
Return the slippage-adjusted execution price.
NoSlippage ¶
Bases: SlippageModel
Fills at the unadjusted base price.
adjusted_price ¶
Return base_price unchanged (see :meth:SlippageModel.adjusted_price).
FixedSpreadSlippage ¶
Bases: SlippageModel
Charges half of a fixed spread (in price units) on each fill.
Initialise the model with a fixed spread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spread
|
float
|
The full bid-ask spread in price units; half is charged on each fill. Must be non-negative. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted adversely by half the fixed spread.
PercentSlippage ¶
Bases: SlippageModel
Applies a fixed percentage slippage to the base price.
Initialise the model with a fractional slippage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
The slippage as a fraction of price (for example
|
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price moved adversely by the configured percentage.
VolatilitySlippage ¶
Bases: SlippageModel
Slippage scaled by recent return volatility.
slippage = coef * sigma * base_price where sigma is the rolling
standard deviation of returns over window bars.
Initialise the model with a volatility coefficient and window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Multiplier applied to the rolling return standard deviation to size the slippage. |
1.0
|
window
|
int
|
Number of recent bars used to estimate volatility. |
20
|
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted by coef * sigma of recent returns.
Source code in src/bbstrader/btengine/friction.py
VolumeParticipationSlippage ¶
Bases: SlippageModel
Slippage proportional to the order's share of bar volume.
Initialise the model with a participation coefficient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Multiplier applied to the order's share of bar volume to size the slippage. |
0.1
|
Source code in src/bbstrader/btengine/friction.py
adjusted_price ¶
Return base_price shifted by the order's share of bar volume.
Source code in src/bbstrader/btengine/friction.py
NoImpact ¶
Bases: MarketImpactModel
No market impact.
SquareRootImpact ¶
Bases: MarketImpactModel
The square-root impact model: impact proportional to sqrt(size / ADV).
impact = coef * base_price * sqrt(|quantity| / adv). Suitable for
institution-scale sizing where impact grows sub-linearly with order size.
Initialise the model with an impact coefficient and ADV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coef
|
float
|
Scales the impact; larger values model thinner books. |
0.1
|
adv
|
float
|
Average daily volume used to normalise order size. Must be positive. |
1000000.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
impact ¶
Return the adverse per-unit impact coef * price * sqrt(|qty|/adv).
Source code in src/bbstrader/btengine/friction.py
ZeroCommission ¶
Bases: CommissionModel
No commission.
FixedCommission ¶
Bases: CommissionModel
A flat fee per fill.
Initialise the model with the flat per-fill fee.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
float
|
The fee charged on every fill, in account currency. |
required |
Source code in src/bbstrader/btengine/friction.py
commission ¶
PerShareCommission ¶
Bases: CommissionModel
A per-share/contract fee with an optional minimum.
Initialise the model with a per-share rate and floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_share
|
float
|
Fee charged per share or contract filled. |
0.005
|
minimum
|
float
|
Minimum commission applied to any fill. |
1.0
|
Source code in src/bbstrader/btengine/friction.py
commission ¶
PercentCommission ¶
Bases: CommissionModel
A commission as a percentage of notional with an optional minimum.
Initialise the model with a notional percentage and floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pct
|
float
|
Fraction of traded notional charged as commission. |
0.001
|
minimum
|
float
|
Minimum commission applied to any fill. |
0.0
|
Source code in src/bbstrader/btengine/friction.py
commission ¶
IBCommission ¶
Bases: CommissionModel
The Interactive Brokers tiered share commission used by FillEvent.
commission ¶
Return the IB tiered per-share commission for the fill.
FundingModel ¶
Bases: ABC
Charges the per-bar carrying cost of holding an open position.
Unlike slippage, impact and commission - which apply once at the fill - a funding model is evaluated every bar a position is held, capturing the overnight/swap financing that dominates the cost of leveraged CFD and FX positions. The returned value is a cash flow (positive = a cost debited from the account, negative = a credit) so a model can charge longs while crediting shorts, or vice versa.
carry
abstractmethod
¶
Return the per-bar carry cash flow for an open position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
The instrument the position is held in. |
required |
quantity
|
float
|
The signed position size; positive for a long, negative for a short. |
required |
price
|
float
|
The current mark-to-market price of one unit. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The cash flow for holding the position over one bar. A |
float
|
positive number is a cost debited from cash; a negative number is a |
|
float
|
credit added to cash. |
Source code in src/bbstrader/btengine/friction.py
NoFunding ¶
Bases: FundingModel
Applies no carrying cost; positions are free to hold.
carry ¶
Return zero carry regardless of the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; present for interface compatibility. |
required |
quantity
|
float
|
Unused; present for interface compatibility. |
required |
price
|
float
|
Unused; present for interface compatibility. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Always |
Source code in src/bbstrader/btengine/friction.py
FixedRateFunding ¶
Bases: FundingModel
A simple cost-of-carry charged as an annual rate on notional.
The per-bar cost is (annual_rate / periods) * quantity * price. Because
quantity is signed, a long position is debited and a short position is
credited at the same rate, mirroring a basic financing model where the
holder of a long leveraged position pays to borrow. Supply short_rate to
charge shorts at a different annual rate (for example a borrow fee that makes
shorting a net cost rather than a credit).
Initialise the model with annualised financing rates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
annual_rate
|
float
|
The annual financing rate applied to long
notional (for example |
required |
periods
|
int
|
The number of bars per year used to convert the
annual rate to a per-bar rate (for example |
252
|
short_rate
|
Optional[float]
|
The annual rate applied to short
notional. When |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/friction.py
carry ¶
Return the per-bar financing cash flow for the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; the rate is instrument independent. |
required |
quantity
|
float
|
The signed position size. |
required |
price
|
float
|
The current mark-to-market price of one unit. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
|
float
|
for positive quantities and |
|
float
|
positive result is debited from cash. |
Source code in src/bbstrader/btengine/friction.py
BrokerSwapFunding ¶
Bases: FundingModel
Per-unit swap points charged each bar, mirroring MT5 swap semantics.
Brokers quote a long and a short swap per lot/unit; this model charges
points * |quantity| * point_value each bar, using the long or short
points according to the sign of the position. Points are expressed as a
cost: positive points are debited from cash and negative points (a positive
swap) are credited.
Initialise the model with the broker's long/short swap points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
long_points
|
float
|
The swap cost per unit per bar applied to long positions. Positive debits cash; negative credits it. |
required |
short_points
|
float
|
The swap cost per unit per bar applied to short
positions, with the same sign convention as |
required |
point_value
|
float
|
The cash value of one swap point per unit, used to convert points to account currency. |
1.0
|
Source code in src/bbstrader/btengine/friction.py
carry ¶
Return the per-bar swap cash flow for the position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
str
|
Unused; swap points are supplied per model instance. |
required |
quantity
|
float
|
The signed position size. |
required |
price
|
float
|
Unused; swap is charged per unit, not on notional. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
|
float
|
long or short swap selected by the sign of |
|
float
|
result is debited from cash. |
Source code in src/bbstrader/btengine/friction.py
apply_friction ¶
apply_friction(base_price: float, direction: str, quantity: float, symbol: str, bardata: DataHandler, slippage: Optional[SlippageModel], impact: Optional[MarketImpactModel]) -> float
Return the effective fill price after slippage and market impact.
Source code in src/bbstrader/btengine/friction.py
optimize ¶
Parameter optimization and walk-forward validation for the backtest engine.
This module turns the replayable data feed (DataHandler.reset() /
n_bars / _records, added when the engine was hardened) into practical
research tooling:
- :func:
optimizeruns a grid or random search over strategy parameters, optionally across processes, and returns a ranked results table. Each worker loads its data once and replays it across every parameter combination viareset()no re-reading or re-downloading per run. - :func:
walk_forwardperforms anchored or rolling walk-forward validation by slicing the in-memory columnar_records, fitting parameters in-sample and scoring them out-of-sample.
Both consume the same BaseStrategy API as live trading, so a strategy is
written once and optimized without modification.
expand_param_grid ¶
expand_param_grid(param_grid: Dict[str, Sequence[Any]], search: str = 'grid', n_iter: Optional[int] = None, seed: int = 0) -> List[Dict[str, Any]]
Expand a parameter grid into a list of concrete parameter dicts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
param_grid
|
Dict[str, Sequence[Any]]
|
Mapping of parameter name to the sequence of values to try. |
required |
search
|
str
|
|
'grid'
|
n_iter
|
Optional[int]
|
Number of combinations to sample when |
None
|
seed
|
int
|
Seed for the random sampler (deterministic by default). |
0
|
Source code in src/bbstrader/btengine/optimize.py
optimize ¶
optimize(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], param_grid: Dict[str, Sequence[Any]], exc_handler: Optional[Type[ExecutionHandler]] = None, initial_capital: float = 100000.0, metric: str = 'sharpe', periods: int = 252, n_jobs: int = 1, search: str = 'grid', n_iter: Optional[int] = None, seed: int = 0, **kwargs: Any) -> pd.DataFrame
Search strategy parameters and return a ranked results table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
Symbols to backtest. |
required |
start_date
|
datetime
|
Backtest start date. |
required |
data_handler
|
Type[DataHandler]
|
A |
required |
strategy
|
Type[Strategy]
|
A |
required |
param_grid
|
Dict[str, Sequence[Any]]
|
Mapping of parameter name to the values to try. |
required |
exc_handler
|
Optional[Type[ExecutionHandler]]
|
Execution handler class (defaults to |
None
|
initial_capital
|
float
|
Starting capital for each run. |
100000.0
|
metric
|
str
|
Column to rank by. One of |
'sharpe'
|
periods
|
int
|
Annualization factor for the Sharpe ratio (252 daily, etc.). |
252
|
n_jobs
|
int
|
Number of worker processes. |
1
|
search
|
str
|
|
'grid'
|
n_iter
|
Optional[int]
|
Sample size for random search. |
None
|
seed
|
int
|
Seed for random search. |
0
|
**kwargs
|
Any
|
Extra keyword args forwarded to every backtest (data handler, strategy, portfolio, execution handler). |
{}
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
A DataFrame with one row per parameter combination plus its metrics, |
DataFrame
|
sorted best-first by |
Source code in src/bbstrader/btengine/optimize.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
walk_forward ¶
walk_forward(symbol_list: List[str], start_date: datetime, data_handler: Type[DataHandler], strategy: Type[Strategy], param_grid: Dict[str, Sequence[Any]], exc_handler: Optional[Type[ExecutionHandler]] = None, initial_capital: float = 100000.0, metric: str = 'sharpe', periods: int = 252, n_splits: int = 3, anchored: bool = True, **kwargs: Any) -> pd.DataFrame
Anchored or rolling walk-forward validation.
The full history is divided into n_splits + 1 equal segments. For each
fold the in-sample window is optimized (in-process), and the best parameter
set is evaluated on the next out-of-sample segment. With anchored=True
the in-sample window always starts at bar 0 and grows; with anchored=False
it rolls forward at a fixed length.
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per fold: the chosen parameters plus the out-of-sample metrics. |
Source code in src/bbstrader/btengine/optimize.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | |
overfitting ¶
Overfitting diagnostics for strategy research.
Implements the Bailey & Lopez de Prado toolkit for distinguishing real alpha from selection bias:
- Probabilistic and Deflated Sharpe ratios adjust an observed Sharpe for sample length, non-normality, and the number of trials that produced it.
- CSCV PBO the probability of backtest overfitting from combinatorially symmetric cross-validation.
- Combinatorial purged cross-validation splits multiple train/test folds with purging/embargo for leakage-free out-of-sample evaluation.
All routines are deterministic and pure NumPy/SciPy.
probabilistic_sharpe_ratio ¶
probabilistic_sharpe_ratio(sharpe: float, n_obs: int, benchmark: float = 0.0, skew: float = 0.0, kurtosis: float = 3.0) -> float
Probability that the true Sharpe exceeds benchmark (PSR).
sharpe and benchmark are per-observation (non-annualized) Sharpe
ratios. skew/kurtosis are the return distribution's moments
(kurtosis 3 == normal).
Source code in src/bbstrader/btengine/overfitting.py
expected_max_sharpe ¶
Expected maximum of n_trials independent Sharpe estimates.
The benchmark a strategy must beat to be considered non-random when it was
selected from n_trials candidates (Bailey & Lopez de Prado).
Source code in src/bbstrader/btengine/overfitting.py
deflated_sharpe_ratio ¶
deflated_sharpe_ratio(sharpe: float, n_obs: int, n_trials: int, sharpe_variance: float, skew: float = 0.0, kurtosis: float = 3.0) -> float
Deflated Sharpe Ratio (DSR).
PSR computed against the expected maximum Sharpe across n_trials, i.e.
the probability the strategy's Sharpe is real after accounting for multiple
testing. sharpe/sharpe_variance are per-observation.
Source code in src/bbstrader/btengine/overfitting.py
cscv_pbo ¶
cscv_pbo(performance: NDArray[float64], n_splits: int = 10, metric: Optional[Callable[[NDArray[float64]], float]] = None) -> float
Probability of Backtest Overfitting via combinatorially symmetric CV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
performance
|
NDArray[float64]
|
A (T, N) matrix of per-observation returns for N candidate configurations over T observations. |
required |
n_splits
|
int
|
Number of disjoint row blocks S (must be even); IS/OOS are all C(S, S/2) balanced partitions. |
10
|
metric
|
Optional[Callable[[NDArray[float64]], float]]
|
Per-configuration score from a sub-matrix of returns. Defaults to the Sharpe ratio. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
PBO in [0, 1]: the fraction of partitions where the in-sample best |
float
|
configuration ranks below the out-of-sample median. |
Source code in src/bbstrader/btengine/overfitting.py
combinatorial_splits ¶
combinatorial_splits(n_obs: int, n_groups: int = 6, n_test_groups: int = 2, embargo: int = 0) -> Iterator[Tuple[NDArray[np.int_], NDArray[np.int_]]]
Yield combinatorial purged cross-validation (CPCV) train/test splits.
Observations are partitioned into n_groups contiguous blocks; every
combination of n_test_groups blocks forms a test set, with the remaining
blocks (minus an embargo band around each test block, to prevent
leakage) as the training set. Yields C(n_groups, n_test_groups) folds.
Source code in src/bbstrader/btengine/overfitting.py
performance ¶
get_asset_performances ¶
get_asset_performances(portfolio: DataFrame, assets: List[str], plot: bool = True, strategy: str = '') -> pd.Series
Calculate the performance of the assets in the portfolio.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
portfolio
|
DataFrame
|
The portfolio DataFrame. |
required |
assets
|
List[str]
|
The list of assets to calculate the performance for. |
required |
plot
|
bool
|
Whether to plot the performance of the assets. |
True
|
strategy
|
str
|
The name of the strategy. |
''
|
Returns:
| Type | Description |
|---|---|
Series
|
pd.Series: The performance of the assets. |
Source code in src/bbstrader/btengine/performance.py
get_perfbased_weights ¶
Calculate the weights of the assets based on their performances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
performances
|
Series
|
The performances of the assets. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dict[str, float]: The weights of the assets. |
Source code in src/bbstrader/btengine/performance.py
create_sharpe_ratio ¶
Create the Sharpe ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
S |
float
|
Sharpe ratio |
Source code in src/bbstrader/btengine/performance.py
create_sortino_ratio ¶
Create the Sortino ratio for the strategy, based on a benchmark of zero (i.e. no risk-free rate information).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
S |
float
|
Sortino ratio |
Source code in src/bbstrader/btengine/performance.py
create_omega_ratio ¶
Create the Omega ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
rf
|
float
|
Risk-free rate. |
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Omega ratio |
Source code in src/bbstrader/btengine/performance.py
create_calmar_ratio ¶
Create the Calmar ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Calmar ratio |
Source code in src/bbstrader/btengine/performance.py
create_tail_ratio ¶
Create the Tail ratio for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Tail ratio |
Source code in src/bbstrader/btengine/performance.py
calculate_risk_metrics ¶
calculate_risk_metrics(returns: Series, benchmark_returns: Series, periods: int = 252) -> Dict[str, float]
Calculate Alpha, Beta and Volatility for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
A pandas Series representing period percentage returns. |
required | |
benchmark_returns
|
A pandas Series representing benchmark period percentage returns. |
required | |
periods
|
int
|
Daily (252), Hourly (2526.5), Minutely(2526.5*60) etc. |
252
|
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dict[str, float]: Alpha, Beta, Volatility |
Source code in src/bbstrader/btengine/performance.py
create_drawdowns ¶
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:
| Name | Type | Description | Default |
|---|---|---|---|
pnl
|
A pandas Series representing period percentage returns. |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
drawdown, duration - high-water mark, duration. |
Source code in src/bbstrader/btengine/performance.py
plot_performance ¶
Plot the performance of the strategy
- (Portfolio value, %)
- (Period returns, %)
- (Drawdowns, %)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
|
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
plot_returns_and_dd ¶
Plot the returns and drawdowns of the strategy compared to a benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The DataFrame containing the strategy returns and drawdowns. |
required |
benchmark
|
str
|
The ticker symbol of the benchmark to compare the strategy to. |
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
plot_monthly_yearly_returns ¶
Plot the monthly and yearly returns of the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
|
required |
title
|
str
|
The title of the plot. |
required |
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
Source code in src/bbstrader/btengine/performance.py
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | |
show_qs_stats ¶
show_qs_stats(returns: Series, benchmark: str, strategy_name: str, save_dir: Optional[str] = None) -> None
Generate the full quantstats report for the strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Serie
|
The DataFrame containing the strategy returns and drawdowns. |
required |
benchmark
|
str
|
The ticker symbol of the benchmark to compare the strategy to. |
required |
strategy_name
|
str
|
The name of the strategy. |
required |
Source code in src/bbstrader/btengine/performance.py
portfolio ¶
Portfolio ¶
Portfolio(bars: DataHandler, events: Queue[Union[OrderEvent, FillEvent, SignalEvent]], start_date: datetime, initial_capital: float = 100000.0, **kwargs: Any)
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.
Initialises the portfolio with bars and an event queue. Also includes a starting datetime index and initial capital (USD unless otherwise stated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bars
|
DataHandler
|
The DataHandler object with current market data. |
required |
events
|
Queue
|
The Event Queue object. |
required |
start_date
|
datetime
|
The start date (bar) of the portfolio. |
required |
initial_capital
|
float
|
The starting capital in USD. |
100000.0
|
kwargs
|
dict
|
Additional arguments
- |
{}
|
Source code in src/bbstrader/btengine/portfolio.py
last_holding
property
¶
The most recently recorded holdings row (mark-to-market equity).
construct_all_positions ¶
Constructs the positions list using the start_date to determine when the time index will begin.
Source code in src/bbstrader/btengine/portfolio.py
construct_all_holdings ¶
Constructs the holdings list using the start_date to determine when the time index will begin.
Source code in src/bbstrader/btengine/portfolio.py
construct_current_holdings ¶
This constructs the dictionary which will hold the instantaneous value of the portfolio across all symbols.
Source code in src/bbstrader/btengine/portfolio.py
update_timeindex ¶
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.
Source code in src/bbstrader/btengine/portfolio.py
update_positions_from_fill ¶
Takes a Fill object and updates the position matrix to reflect the new position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fill
|
FillEvent
|
The Fill object to update the positions with. |
required |
Source code in src/bbstrader/btengine/portfolio.py
update_holdings_from_fill ¶
Takes a Fill object and updates the holdings matrix to reflect the holdings value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fill
|
FillEvent
|
The Fill object to update the holdings with. |
required |
Source code in src/bbstrader/btengine/portfolio.py
update_fill ¶
Updates the portfolio current positions and holdings from a FillEvent.
Source code in src/bbstrader/btengine/portfolio.py
generate_order ¶
Turns a SignalEvent into an OrderEvent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
SignalEvent
|
The tuple containing Signal information. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
OrderEvent |
Optional[OrderEvent]
|
The OrderEvent to be executed. |
Source code in src/bbstrader/btengine/portfolio.py
update_signal ¶
Acts on a SignalEvent to generate new orders based on the portfolio logic.
Source code in src/bbstrader/btengine/portfolio.py
create_equity_curve_dataframe ¶
Creates a pandas DataFrame from the all_holdings list of dictionaries.
Source code in src/bbstrader/btengine/portfolio.py
output_summary_stats ¶
Creates a list of summary statistics for the portfolio.
Source code in src/bbstrader/btengine/portfolio.py
strategy ¶
BacktestStrategy ¶
BacktestStrategy(events: Queue[Union[SignalEvent, FillEvent]], symbol_list: List[str], bars: DataHandler, **kwargs: Any)
Bases: BaseStrategy
Strategy implementation specifically for Backtesting. Handles internal state for orders, positions, trades, and cash. Simulates order execution and pending orders.
Initialize the BacktestStrategy object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
The event queue. |
required | |
symbol_list
|
The list of symbols for the strategy. |
required | |
bars
|
The data handler object. |
required | |
**kwargs
|
Additional keyword arguments for other classes (e.g, Portfolio, ExecutionHandler). - max_trades : The maximum number of trades allowed per symbol. - time_frame : The time frame for the strategy. - logger : The logger object for the strategy. |
required |
Source code in src/bbstrader/btengine/strategy.py
orders
property
¶
The pending orders per symbol, keyed by order type.
trades
property
¶
The executed trade counts per symbol, keyed by side.
positions
property
¶
The open position sizes per symbol, keyed by LONG/SHORT.
holdings
property
¶
The current mark-to-market holdings value per symbol.
get_update_from_portfolio ¶
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:
| Name | Type | Description | Default |
|---|---|---|---|
positions
|
The positions for the symbols in the strategy. |
required | |
holdings
|
The holdings for the symbols in the strategy. |
required |
Source code in src/bbstrader/btengine/strategy.py
update_trades_from_fill ¶
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.
Source code in src/bbstrader/btengine/strategy.py
get_asset_values ¶
get_asset_values(symbol_list: List[str], window: int, value_type: str = 'returns', array: bool = True, **kwargs) -> Optional[Dict[str, Union[np.typing.NDArray, pd.Series]]]
Return the last window values of value_type for each symbol.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol_list
|
List[str]
|
The symbols to fetch values for. |
required |
window
|
int
|
The number of most-recent bars required per symbol. |
required |
value_type
|
str
|
The bar field to read (for example |
'returns'
|
array
|
bool
|
When True return NumPy arrays (NaNs dropped); when False return pandas Series sliced from the bar DataFrame. |
True
|
kwargs
|
Unused; accepted for forward compatibility. |
{}
|
Returns:
| Type | Description |
|---|---|
Optional[Dict[str, Union[NDArray, Series]]]
|
Optional[Dict[str, Union[NDArray, pd.Series]]]: A mapping of symbol |
Optional[Dict[str, Union[NDArray, Series]]]
|
to its last |
Optional[Dict[str, Union[NDArray, Series]]]
|
|
Source code in src/bbstrader/btengine/strategy.py
calculate_signals
abstractmethod
¶
Compute trading signals for the current bar.
Subclasses implement their strategy logic here, placing orders via the
buy_mkt/sell_mkt/close_positions helpers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event for the current bar. |
required |
Source code in src/bbstrader/btengine/strategy.py
buy_mkt ¶
buy_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a long position
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_mkt ¶
sell_mkt(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a short position
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
close_positions ¶
close_positions(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Close a position or exit all positions
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_stop ¶
buy_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a stop price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_stop ¶
sell_stop(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a stop price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_limit ¶
buy_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_limit ¶
sell_limit(id: int, symbol: str, price: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
buy_stop_limit ¶
buy_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to buy at a stop-limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
sell_stop_limit ¶
sell_stop_limit(id: int, symbol: str, price: float, stoplimit: float, quantity: int, strength: float = 1.0, dtime: Optional[Union[datetime, Timestamp]] = None) -> None
Open a pending order to sell at a stop-limit price
See bbstrader.btengine.event.SignalEvent for more details on arguments.
Source code in src/bbstrader/btengine/strategy.py
check_pending_orders ¶
Check for pending orders and handle them accordingly.
Source code in src/bbstrader/btengine/strategy.py
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | |
MultiStrategy ¶
Runs several strategies against one shared portfolio, cash account and clock.
The engine sees a single strategy; this adapter fans every engine callback
out to each child strategy. All children post signals to the same event
queue, so the shared Portfolio nets their positions and allocates one
pool of capital enabling cross-strategy capital-allocation and netting
tests that a single-strategy engine cannot express.
Children are typically scoped to disjoint symbol sets; when they overlap, positions net at the portfolio level and each child's trade counters track its own fills for symbols it trades.
Wrap one or more child strategies behind a single engine interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
strategies
|
List[BacktestStrategy]
|
The child strategies to run against the shared portfolio. The union of their symbols becomes this adapter's symbol set. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/strategy.py
calculate_signals ¶
Fan the market event out to every child strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event for the current bar. |
required |
Source code in src/bbstrader/btengine/strategy.py
check_pending_orders ¶
get_update_from_portfolio ¶
Push the latest portfolio positions and holdings to each child.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
positions
|
Dict[str, float]
|
Current position sizes per symbol. |
required |
holdings
|
Dict[str, float]
|
Current holdings value per symbol. |
required |
Source code in src/bbstrader/btengine/strategy.py
templates ¶
Ready-to-use strategy templates (a small cookbook).
These are concrete, parameterized BacktestStrategy subclasses for the most
common archetypes: trend following (SMA crossover), mean reversion (RSI), and
breakout (Donchian channel). They are built entirely on the shared strategy API
-- get_asset_values for data, the vectorized
:mod:bbstrader.core.indicators for signals, and the buy_mkt/close_positions
order helpers so they are also natural targets for
:func:bbstrader.btengine.optimize.optimize.
Each template trades a single long position per symbol and is long-only, which keeps them simple to read and to optimize. Subclass or copy them as a starting point for your own ideas.
SMACrossoverStrategy ¶
Bases: _TemplateBase
Trend following: go long when the fast SMA crosses above the slow SMA.
kwargs
fast (int, default 10): Fast SMA window. slow (int, default 30): Slow SMA window. quantity (int, default 100): Units per trade.
Initialise the SMA crossover with fast/slow windows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Enter long on an up-cross and exit on a down-cross of the SMAs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
RSIMeanReversionStrategy ¶
Bases: _TemplateBase
Mean reversion: buy when RSI is oversold, exit when it recovers.
kwargs
period (int, default 14): RSI lookback. oversold (float, default 30): Entry threshold. exit_level (float, default 55): Exit threshold. quantity (int, default 100): Units per trade.
Initialise the RSI mean-reversion thresholds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Buy when RSI is oversold and exit when it recovers above the level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
DonchianBreakoutStrategy ¶
Bases: _TemplateBase
Breakout: go long when price closes above the prior N-bar high.
The channel is taken from the previous bar to avoid look-ahead. Exit when price closes below the prior N-bar low.
kwargs
window (int, default 20): Donchian channel lookback. quantity (int, default 100): Units per trade.
Initialise the Donchian breakout channel lookback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Any
|
The engine event queue. |
required |
symbol_list
|
List[str]
|
The symbols traded by the strategy. |
required |
bars
|
Any
|
The DataHandler providing market data. |
required |
kwargs
|
Any
|
|
{}
|
Source code in src/bbstrader/btengine/templates.py
calculate_signals ¶
Go long on a close above the prior N-bar high; exit below the low.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
MarketEvent
|
The market event driving the bar; ignored unless it is a MARKET event. |
required |
Source code in src/bbstrader/btengine/templates.py
timeframe ¶
Multi-timeframe support: derive higher-timeframe bars from a base feed.
A common institutional pattern is to execute on a fast timeframe (e.g. 1m)
while computing signals on a slower one (e.g. H1 or daily). Rather than
rearchitecting the event loop into a full multi-clock model, this module lets a
strategy resample the base-timeframe bars it already receives into completed
higher-timeframe (HTF) bars on demand inside calculate_signals with no
look-ahead.
MultiTimeFrame wraps a DataHandler; resample_ohlcv is the underlying
aggregation and can be used standalone on any OHLCV frame.
MultiTimeFrame ¶
Derive completed higher-timeframe bars from a base-timeframe DataHandler.
Use inside a strategy's calculate_signals to read slow-timeframe context
while executing on the fast base feed::
mtf = MultiTimeFrame(self.data)
daily_close = mtf.htf_value(symbol, "D1") # last *completed* daily close
Wrap a base-timeframe DataHandler for higher-timeframe access.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataHandler
|
The base-timeframe data feed to resample from. |
required |
lookback
|
int
|
Default number of base bars to pull when resampling. |
1000
|
Source code in src/bbstrader/btengine/timeframe.py
htf_bars ¶
htf_bars(symbol: str, rule: str, n: Optional[int] = None, lookback: Optional[int] = None, drop_partial: bool = True) -> pd.DataFrame
Return resampled HTF bars for symbol.
With drop_partial (default) the final, possibly still-forming bucket
is dropped so only completed HTF bars are visible preventing
look-ahead. n limits the result to the most recent n bars.
Source code in src/bbstrader/btengine/timeframe.py
htf_value ¶
htf_value(symbol: str, rule: str, val_type: str = 'close', lookback: Optional[int] = None, drop_partial: bool = True) -> Optional[float]
Latest completed HTF value for symbol (None if not enough data).
Source code in src/bbstrader/btengine/timeframe.py
resample_ohlcv ¶
resample_ohlcv(df: DataFrame, rule: str, *, label: str = 'left', closed: str = 'left') -> pd.DataFrame
Aggregate an OHLCV DataFrame up to a higher timeframe.
open=first, high=max, low=min, close=last, volume=sum (adj_close=last when
present). Buckets with no data are dropped. df must have a DatetimeIndex.
Source code in src/bbstrader/btengine/timeframe.py
vectorized ¶
A vectorized research fast-path backtester.
This is the "does this even have alpha?" loop: it evaluates entry/exit signal
arrays across the entire history with NumPy, with no event queue and no
path-dependent order state. It is for fast hypothesis screening over many
parameter combinations orders of magnitude faster than the event-driven
engine not for faithful order-state simulation (use BacktestEngine for
that). The two share the same data: feed it the columnar arrays from a
DataHandler (or any price series).
Signals are boolean arrays aligned to the price series; an indicator from
:mod:bbstrader.core.indicators plugs in directly.
VectorizedResult
dataclass
¶
VectorizedResult(equity: NDArray[float64], returns: NDArray[float64], position: NDArray[float64], trades: List[Tuple[int, int]], init_cash: float, periods: int)
Result of a vectorized backtest with lazily computed metrics.
total_return
property
¶
The total return over the run as a fraction of initial capital.
max_drawdown
property
¶
Largest peak-to-trough drawdown of the equity curve (as a fraction).
win_rate
property
¶
The fraction of trades whose equity rose between entry and exit.
to_frame ¶
Return the run as a DataFrame of position, returns and equity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
Optional[Index]
|
An optional index (for example the price series' DatetimeIndex) to label the rows. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Columns |
Source code in src/bbstrader/btengine/vectorized.py
summary ¶
Return a dict of the headline metrics for the run.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
|
dict
|
|
Source code in src/bbstrader/btengine/vectorized.py
vectorized_backtest ¶
vectorized_backtest(close: ArrayLike, entries: ArrayLike, exits: ArrayLike, *, short_entries: Optional[ArrayLike] = None, short_exits: Optional[ArrayLike] = None, allow_short: bool = False, init_cash: float = 100000.0, fees: float = 0.0, slippage: float = 0.0, periods: int = 252) -> VectorizedResult
Run a fully vectorized signal backtest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
close
|
ArrayLike
|
Price series. |
required |
entries
|
ArrayLike
|
Boolean array; True opens a long position. |
required |
exits
|
ArrayLike
|
Boolean array; True closes the long position. |
required |
short_entries / short_exits
|
Optional short-side signals (require
|
required | |
allow_short
|
bool
|
Permit short positions. |
False
|
init_cash
|
float
|
Starting capital. |
100000.0
|
fees
|
float
|
Per-unit-turnover fee as a fraction of notional (e.g. 0.0005). |
0.0
|
slippage
|
float
|
Per-unit-turnover slippage as a fraction of notional. |
0.0
|
periods
|
int
|
Annualization factor for the Sharpe ratio. |
252
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
VectorizedResult
|
class: |