Source code for backtrader.observers.buysell

#!/usr/bin/env python
"""BuySell Observer Module - Buy/sell signal visualization.

This module provides the BuySell observer for visualizing buy and sell
orders on the chart.

Classes:
    BuySell: Observer that plots buy/sell markers on the chart.

Example:
    >>> cerebro = bt.Cerebro()
    >>> cerebro.addobserver(bt.observers.BuySell)
"""

import math

from ..observer import Observer


# Buy and sell point markers
[docs] class BuySell(Observer): """ This observer keeps track of the individual buy/sell orders (individual executions) and will plot them on the chart along the data around the execution price level Params: - ``barplot`` (default: ``False``) Plot buy signals below the minimum and sell signals above the maximum. If `False`, it will plot on the average price of executions during a bar - ``bardist`` (default: ``0.015`` 1.5%) Distance to max/min when ``barplot`` is ``True`` """ lines = ( "buy", "sell", ) plotinfo = {"plot": True, "subplot": False, "plotlinelabels": True} plotlines = { "buy": {"marker": "^", "markersize": 8.0, "color": "lime", "fillstyle": "full", "ls": ""}, "sell": {"marker": "v", "markersize": 8.0, "color": "red", "fillstyle": "full", "ls": ""}, } params = ( ("barplot", False), # plot above/below max/min for clarity in bar plot ("bardist", 0.015), # distance to max/min in absolute perc ) def __init__(self): """Initialize the BuySell observer. Sets up tracking for buy/sell order lengths. """ self.curbuylen = 0 self.curselllen = 0 self._lastbar = None
[docs] def next(self): """Update buy/sell markers based on executed orders. Calculates average prices for buy and sell orders during the bar. """ try: barref = self.data.datetime[0] except (IndexError, AttributeError, TypeError): barref = None sentinel = object() if getattr(self, "_lastbar", sentinel) != barref: self.lines.buy[0] = float("nan") self.lines.sell[0] = float("nan") self.curbuylen = 0 self.curselllen = 0 self._lastbar = barref buy = [] sell = [] # If there are pending orders for order in self._owner._orderspending: # If no data or size is 0, skip if order.data is not self.data or not order.executed.size: continue # If it's a buy order, add price to buy, if it's a sell order, add price to sell if order.isbuy(): buy.append(order.executed.price) else: sell.append(order.executed.price) # Take into account replay ... something could already be in there # Write down the average buy/sell price # BUY # Get buy price curbuy = self.lines.buy[0] # If NaN, curbuy equals 0, curbuylen=0, otherwise, curbuylen = self.curbuylen if curbuy != curbuy: # NaN curbuy = 0.0 self.curbuylen = curbuylen = 0 else: curbuylen = self.curbuylen # Current total price buyops = curbuy + math.fsum(buy) # Current total order count buylen = curbuylen + len(buy) # Calculate average price value = buyops / float(buylen or "NaN") # If not plotting, get average price, if plotting, get a percentage of lowest price for better display if not self.p.barplot: self.lines.buy[0] = value elif value == value: # Not NaN pbuy = self.data.low[0] * (1 - self.p.bardist) self.lines.buy[0] = pbuy # Update buylen values curbuy = buyops self.curbuylen = buylen # For sell orders, similar logic # SELL cursell = self.lines.sell[0] if cursell != cursell: # NaN cursell = 0.0 self.curselllen = curselllen = 0 else: curselllen = self.curselllen sellops = cursell + math.fsum(sell) selllen = curselllen + len(sell) value = sellops / float(selllen or "NaN") if not self.p.barplot: self.lines.sell[0] = value elif value == value: # Not NaN psell = self.data.high[0] * (1 + self.p.bardist) self.lines.sell[0] = psell # Update selllen values cursell = sellops self.curselllen = selllen