backtrader.analyzers.transactions 源代码

#!/usr/bin/env python
"""Transactions Analyzer Module - Transaction logging.

This module provides the Transactions analyzer for recording all
transactions (order executions) during backtesting.

Classes:
    Transactions: Analyzer that records transaction history.

Example:
    >>> cerebro = bt.Cerebro()
    >>> cerebro.addanalyzer(bt.analyzers.Transactions, _name='txn')
    >>> results = cerebro.run()
    >>> print(results[0].analyzers.txn.get_analysis())
"""

import collections

from ..analyzer import Analyzer
from ..order import Order
from ..position import Position


# Transactions
[文档] class Transactions(Analyzer): """This analyzer reports the transactions occurred with each every data in the system It looks at the order execution bits to create a `Position` starting from 0 during each `next` cycle. The result is used during next to record the transactions Params: - Headers (default: ``True``) Add an initial key to the dictionary holding the results with the names of the datas This analyzer was modeled to facilitate the integration with ``pyfolio``, and the header names are taken from the samples used for it:: 'Date', 'amount', 'price', 'sid', 'symbol', 'value' Methods: - Get_analysis Returns a dictionary with returns as values and the datetime points for each return as keys """ # Parameters params = ( ("headers", False), ("_pfheaders", ("date", "amount", "price", "sid", "symbol", "value")), ) # Initialize
[文档] def __init__(self, *args, **kwargs): """Initialize the Transactions analyzer. Args: *args: Positional arguments. **kwargs: Keyword arguments for analyzer parameters. """ # CRITICAL FIX: Call super().__init__() first to initialize self.p super().__init__(*args, **kwargs) self._idnames = None self._positions = None
[文档] def start(self): """Initialize the analyzer at the start of the backtest. Sets up the results structure and initializes position tracking for each data feed. """ super().start() # If headers is True, initialize rets if self.p.headers: self.rets[self.p._pfheaders[0]] = [list(self.p._pfheaders[1:])] # Positions self._positions = collections.defaultdict(Position) # Index and data names self._idnames = list(enumerate(self.strategy.getdatanames()))
# Order information processing
[文档] def notify_order(self, order): """Process order execution notifications. Updates the position tracking when orders are executed or partially executed. Collected positions are recorded in the next() method. Args: order: The order object with execution information. """ # An order could have several partial executions per cycle (unlikely # but possible) and therefore: collect each new execution notification # and let the work for the next # We use a fresh Position object for each round to get a summary of what # the execution bits have done in that round # If order is not executed, ignore if order.status not in [Order.Partial, Order.Completed]: return # It's not an execution # Get position of the data that generated the order pos = self._positions[order.data._name] # Loop for exbit in order.executed.iterpending(): # If execution info is None, break if exbit is None: break # end of pending reached # Update position information pos.update(exbit.size, exbit.price)
# Called once per bar
[文档] def next(self): """Record transactions for the current bar. Collects position changes from all data feeds and records them in the results dictionary keyed by datetime. """ # super(Transactions, self).next() # let dtkey update # Entries entries = [] # For index and data names for i, dname in self._idnames: # Get position of the data pos = self._positions.get(dname, None) # If position is not None, if position is not 0, save position related data if pos is not None: size, price = pos.size, pos.price if size: entries.append([size, price, i, dname, -size * price]) # If position is not 0, update current bar's position data if entries: self.rets[self.strategy.datetime.datetime()] = entries # Clear self._positions self._positions.clear()