pybroker.portfolio module

Contains portfolio related functionality, such as portfolio metrics and placing orders.

class Entry(id: int, date: ~numpy.datetime64, symbol: str, shares: ~decimal.Decimal, price: ~decimal.Decimal, type: ~typing.Literal['long', 'short'], bars: int = 0, sym_bars: int = 0, stops: list[~pybroker.portfolio.Stop] = <factory>, mae: ~decimal.Decimal = <factory>, mfe: ~decimal.Decimal = <factory>)[source]

Bases: object

Contains information about an entry into a Position.

id

Unique identifier.

Type:

int

date

Date of the entry.

Type:

numpy.datetime64

symbol

Symbol of the entry.

Type:

str

shares

Number of shares.

Type:

decimal.Decimal

price

Share price of the entry.

Type:

decimal.Decimal

type

Type of Position, either long or short.

Type:

Literal[‘long’, ‘short’]

bars

Current number of bars since entry, counted on the portfolio’s clock: every bar of the backtest, whether or not symbol traded on it. Feeds Trade.bars and pnl_per_bar, which measure elapsed holding time.

Type:

int

sym_bars

Current number of symbol’s own bars since entry. Advances only on dates symbol has a bar, so it matches what pybroker.context.ExecContext.bars reports. This is what pybroker.common.StopType.BAR stops count against: when calendars are ragged, bars would expire a hold_bars stop after fewer of the symbol’s own bars than the caller asked for.

Type:

int

stops

Stops set on the entry.

Type:

list[pybroker.portfolio.Stop]

mae

Maximum adverse excursion (MAE).

Type:

decimal.Decimal

mfe

Maximum favorable excursion (MFE).

Type:

decimal.Decimal

class Order(id: int, type: Literal['buy', 'sell'], symbol: str, date: datetime64, created: datetime64 | None, order_type: Literal['market', 'limit', 'stop_bar', 'stop_loss', 'stop_profit', 'stop_trailing'], intent: Literal['buy_to_open', 'buy_to_close', 'sell_to_open', 'sell_to_close'], shares: Decimal, limit_price: Decimal | None, market_price: Decimal, fill_price: Decimal, fees: Decimal)[source]

Bases: NamedTuple

Holds information about a filled order.

id

Unique identifier.

Type:

int

type

Type of order, either buy or sell.

Type:

Literal[‘buy’, ‘sell’]

symbol

Ticker symbol of the order.

Type:

str

date

Date the order was filled.

Type:

numpy.datetime64

created

Date the order signal was created, or None for stop-triggered orders.

Type:

numpy.datetime64 | None

order_type

How the order originated, either market, limit, stop_bar, stop_loss, stop_profit, or stop_trailing.

Type:

Literal[‘market’, ‘limit’, ‘stop_bar’, ‘stop_loss’, ‘stop_profit’, ‘stop_trailing’]

intent

Position intent, either buy_to_open, buy_to_close, sell_to_open, or sell_to_close.

Type:

Literal[‘buy_to_open’, ‘buy_to_close’, ‘sell_to_open’, ‘sell_to_close’]

shares

Number of shares bought or sold.

Type:

decimal.Decimal

limit_price

Limit price that was used for the order.

Type:

decimal.Decimal | None

market_price

Market price prevailing when the order was filled, before any fill-time slippage adjustment. Equal to fill_price when no slippage model is set.

Type:

decimal.Decimal

fill_price

Price that the order was filled at.

Type:

decimal.Decimal

fees

Brokerage fees for order.

Type:

decimal.Decimal

class Portfolio(cash: float, fee_mode: FeeMode | Callable[[FeeInfo], Decimal] | None = None, fee_amount: float | None = None, enable_fractional_shares: bool = False, position_mode: PositionMode = PositionMode.DEFAULT, max_long_positions: int | None = None, max_short_positions: int | None = None, record_stops: bool | None = False, leverage: float = 1.0, interest_rate: float = 0.0, bars_per_year: int | None = None, record_portfolio_bars: bool = False, record_position_bars: bool = False)[source]

Bases: object

Class representing a portfolio of holdings. The portfolio contains information about open positions and balances, and is also used to place buy and sell orders.

Parameters:
  • cash – Starting cash balance.

  • fee_mode – Brokerage fee mode.

  • fee_amount – Brokerage fee amount.

  • enable_fractional_shares – Whether to enable trading fractional shares.

  • position_mode – Position mode for Portfolio.

  • max_long_positions – Maximum number of long Positions that can be held at a time. If None, then unlimited.

  • max_short_positions – Maximum number of short Positions that can be held at a time. If None, then unlimited.

  • record_stops – Whether to record stop data per-bar.

cash

Current cash balance.

equity

Current amount of equity, defined as the net cash balance plus the market value of all open long positions plus the collateral posted for all open short positions. Short positions are held at cost, so their unrealized PnL is excluded.

market_value

Current market value. The market value is defined as equity added together with the unrealized PnL of all open short positions.

fees

Current brokerage fees.

fee_amount

Brokerage fee amount.

enable_fractional_shares

Whether to enable trading fractional shares.

orders

deque of all filled orders, sorted in ascending chronological order.

margin

Notional exposure of open short positions at mark.

margin_loan

Borrowed funds used for leveraged long and short positions.

pnl

Realized profit and loss (PnL).

long_positions

dict mapping ticker symbols to open long Positions.

short_positions

dict mapping ticker symbols to open short Positions.

symbols

Ticker symbols of all currently open positions.

bars

deque of snapshots of Portfolio state on every bar, sorted in ascending chronological order.

position_bars

deque of snapshots of Position states on every bar, sorted in ascending chronological order.

win_rate

Running win rate of trades.

loss_rate

Running loss rate of trades.

buy(date: datetime64, symbol: str, shares: Decimal, fill_price: Decimal, limit_price: Decimal | None = None, stops: Iterable[Stop] | None = None, created: datetime64 | None = None, order_type: OrderType = OrderType.MARKET, market_price: Decimal | None = None) Order | None[source]

Places a buy order.

Parameters:
  • date – Date when the Order is placed.

  • symbol – Ticker symbol to buy.

  • shares – Number of shares to buy.

  • fill_price – If filled, the price used to fill the Order.

  • limit_price – Limit price of the Order.

  • stopsStops to set on the Entry created from the Order, if filled.

  • created – Date the order signal was created.

  • order_type – How the order originated.

  • market_price – Market price at fill time, before fill-time slippage. Defaults to fill_price.

Returns:

Order if the order was filled, otherwise None.

capture_bar(date: datetime64, col_scope: ColumnScope, sym_end_index: Mapping[str, int], price_scope: PriceScope | None = None)[source]

Captures portfolio state of the current bar.

check_stops(date: datetime64, price_scope: PriceScope, col_scope: ColumnScope | None = None, sym_end_index: Mapping[str, int] | None = None, ind_scope: IndicatorScope | None = None, slippage_model: SlippageModel | None = None)[source]

Checks whether stops are triggered.

Price stops on one entry are evaluated in ascending Stop.id order and the first one to trigger exits the entry. Ids are assigned when pybroker.context.ExecContext.to_result() builds the stops, in a fixed order – loss, then profit, then trailing – not in the order the attributes were assigned on the context, so precedence is deterministic but not caller-controlled.

pybroker.common.StopType.BAR stops are evaluated after the price stops, whatever their id, because a bar-count exit and a price exit landing on the same bar have no true ordering: the bar’s price path decides, and it is not modeled here.

When slippage_model is set, triggered stops fill at the adjusted price. Slippage never affects whether a stop triggers, and share adjustments are ignored because a stop exits its entry in full.

exit_position(date: datetime64, symbol: str, buy_fill_price: Decimal, sell_fill_price: Decimal, col_scope: ColumnScope | None = None, ind_scope: IndicatorScope | None = None, sym_end_index: Mapping[str, int] | None = None, slippage_model: SlippageModel | None = None)[source]

Exits any long and short positions for symbol at buy_fill_price and sell_fill_price.

When slippage_model is set, both fills are adjusted by it. Share adjustments are ignored because the positions are exited in full. The unadjusted prices are recorded as market_price on the resulting Orders.

incr_bars(date: datetime64 | None = None, price_scope: PriceScope | None = None)[source]

Increments the number of bars held by every trade entry.

Entry.bars advances on every bar of the backtest. When date and price_scope are given, Entry.sym_bars advances only on dates the entry’s symbol has a bar of its own, which is what pybroker.common.StopType.BAR stops count against. Without them both counters advance together, matching a calendar where every symbol trades on every bar.

remove_stop(stop_id: int) bool[source]

Removes a Stop with stop_id.

remove_stops(val: str | Position | Entry, stop_type: StopType | None = None)[source]

Removes Stops.

Parameters:
sell(date: datetime64, symbol: str, shares: Decimal, fill_price: Decimal, limit_price: Decimal | None = None, stops: Iterable[Stop] | None = None, created: datetime64 | None = None, order_type: OrderType = OrderType.MARKET, market_price: Decimal | None = None) Order | None[source]

Places a sell order.

Parameters:
  • date – Date when the Order is placed.

  • symbol – Ticker symbol to sell.

  • shares – Number of shares to sell.

  • fill_price – If filled, the price used to fill the Order.

  • limit_price – Limit price of the Order.

  • stopsStops to set on the Entry created from the Order, if filled.

  • created – Date the order signal was created.

  • order_type – How the order originated.

  • market_price – Market price at fill time, before fill-time slippage. Defaults to fill_price.

Returns:

Order if the order was filled, otherwise None.

class PortfolioBar(date: datetime64, cash: Decimal, equity: Decimal, notional: Decimal, margin: Decimal, margin_loan: Decimal, net_cash_balance: Decimal, market_value: Decimal, pnl: Decimal, unrealized_pnl: Decimal, fees: Decimal)[source]

Bases: NamedTuple

Snapshot of Portfolio state, captured per bar.

date

Date of bar.

Type:

numpy.datetime64

cash

Available cash in Portfolio.

Type:

decimal.Decimal

equity

Amount of equity in Portfolio. Open short positions are held at cost, so their unrealized PnL is excluded.

Type:

decimal.Decimal

notional

Notional exposure of all open positions at mark: the market value of open long positions plus the margin of open short positions.

Type:

decimal.Decimal

margin

Notional exposure of open short positions at mark.

Type:

decimal.Decimal

margin_loan

Borrowed funds used for leveraged long and short positions.

Type:

decimal.Decimal

net_cash_balance

cash - margin_loan.

Type:

decimal.Decimal

market_value

Market value of Portfolio, equal to equity plus the unrealized PnL of all open short positions.

Type:

decimal.Decimal

pnl

Realized profit and loss (PnL) of Portfolio.

Type:

decimal.Decimal

unrealized_pnl

Unrealized profit and loss (PnL) of Portfolio, equal to market_value - equity.

Type:

decimal.Decimal

fees

Brokerage fees.

Type:

decimal.Decimal

class Position(symbol: str, shares: ~decimal.Decimal, type: ~typing.Literal['long', 'short'], close: ~decimal.Decimal = <factory>, equity: ~decimal.Decimal = <factory>, market_value: ~decimal.Decimal = <factory>, margin: ~decimal.Decimal = <factory>, pnl: ~decimal.Decimal = <factory>, entries: ~collections.deque[~pybroker.portfolio.Entry] = <factory>, bars: int = 0, entry_notional: ~decimal.Decimal = <factory>, unmarked_shares: ~decimal.Decimal = <factory>, unmarked_notional: ~decimal.Decimal = <factory>)[source]

Bases: object

Contains information about an open position in symbol.

symbol

Ticker symbol of the position.

Type:

str

shares

Number of shares.

Type:

decimal.Decimal

type

Type of position, either long or short.

Type:

Literal[‘long’, ‘short’]

close

Last close price of symbol.

Type:

decimal.Decimal

equity

Equity in the position.

Type:

decimal.Decimal

market_value

Market value of position.

Type:

decimal.Decimal

margin

Amount of margin in position.

Type:

decimal.Decimal

pnl

Unrealized profit and loss (PnL).

Type:

decimal.Decimal

entries

deque of position Entrys sorted in ascending chronological order.

Type:

collections.deque[pybroker.portfolio.Entry]

bars

Current number of bars since entry, on the portfolio’s clock.

Type:

int

entry_notional

Total cost basis of the position’s open entries.

Type:

decimal.Decimal

unmarked_shares

Shares acquired since the last mark. Valuing these at close would price them at a mark taken before they were bought, so they are held at cost until the next mark.

Type:

decimal.Decimal

unmarked_notional

Cost of unmarked_shares, at fill price.

Type:

decimal.Decimal

class PositionBar(symbol: str, date: datetime64, long_shares: Decimal, short_shares: Decimal, close: Decimal, equity: Decimal, market_value: Decimal, margin: Decimal, unrealized_pnl: Decimal)[source]

Bases: NamedTuple

Snapshot of an open Position‘s state, captured per bar.

symbol

Ticker symbol of Position.

Type:

str

date

Date of bar.

Type:

numpy.datetime64

long_shares

Number of shares long in Position.

Type:

decimal.Decimal

short_shares

Number of shares short in Position.

Type:

decimal.Decimal

close

Last close price of symbol.

Type:

decimal.Decimal

equity

Amount of equity in Position.

Type:

decimal.Decimal

market_value

Market value of Position.

Type:

decimal.Decimal

margin

Amount of margin in Position.

Type:

decimal.Decimal

unrealized_pnl

Unrealized profit and loss (PnL) of Position.

Type:

decimal.Decimal

class Stop(id: int, symbol: str, stop_type: StopType, pos_type: Literal['long', 'short'], percent: Decimal | None, points: Decimal | None, bars: int | None, fill_price: int | float | floating | Decimal | PriceType | Callable[[str, BarData], int | float | Decimal] | None, limit_price: Decimal | None, exit_price: PriceType | None)[source]

Bases: NamedTuple

Contains information about a stop set on Entry.

id

Unique identifier.

Type:

int

symbol

Symbol of the stop.

Type:

str

stop_type

StopType.

Type:

pybroker.common.StopType

pos_type

Type of Position, either long or short.

Type:

Literal[‘long’, ‘short’]

percent

Percent from entry price.

Type:

decimal.Decimal | None

points

Cash amount from entry price.

Type:

decimal.Decimal | None

bars

Number of bars after which to trigger the stop.

Type:

int | None

fill_price

Price that the stop will be filled at.

Type:

int | float | numpy.floating | decimal.Decimal | pybroker.common.PriceType | Callable[[str, pybroker.common.BarData], int | float | decimal.Decimal] | None

limit_price

Limit price to use for the stop.

Type:

decimal.Decimal | None

exit_price

Exit pybroker.common.PriceType to use for the stop exit. If set, the stop is checked against the exit_price and exits at the exit_price when triggered.

Type:

pybroker.common.PriceType | None

class StopRecord(date: datetime64, symbol: str, stop_id: int, stop_type: str, pos_type: Literal['long', 'short'], curr_value: Decimal | None, curr_bars: int | None, percent: Decimal | None, points: Decimal | None, bars: int | None, fill_price: Decimal | None, limit_price: Decimal | None, exit_price: PriceType | None)[source]

Bases: NamedTuple

Records per-bar data about a stop.

date

Date of the bar.

Type:

numpy.datetime64

symbol

Symbol of the stop.

Type:

str

stop_id

Unique identifier.

Type:

int

stop_type

StopType.

Type:

str

pos_type

Type of Position, either long or short.

Type:

Literal[‘long’, ‘short’]

curr_value

Current value of the stop.

Type:

decimal.Decimal | None

curr_bars

Current bars of the stop.

Type:

int | None

percent

Percent from entry price.

Type:

decimal.Decimal | None

points

Cash amount from entry price.

Type:

decimal.Decimal | None

bars

Number of bars after which to trigger the stop.

Type:

int | None

fill_price

Price that the stop will be filled at.

Type:

decimal.Decimal | None

limit_price

Limit price to use for the stop.

Type:

decimal.Decimal | None

exit_price

Exit pybroker.common.PriceType to use for the stop exit. If set, the stop is checked against the exit_price and exits at the exit_price when triggered.

Type:

pybroker.common.PriceType | None

class Trade(id: int, type: Literal['long', 'short'], symbol: str, entry_date: datetime64, exit_date: datetime64, entry: Decimal, exit: Decimal, shares: Decimal, pnl: Decimal, return_pct: Decimal, agg_pnl: Decimal, bars: int, pnl_per_bar: Decimal, stop: Literal['bar', 'loss', 'profit', 'trailing'] | None, mae: Decimal, mfe: Decimal)[source]

Bases: NamedTuple

Holds information about a completed trade (entry and exit).

id

Unique identifier.

Type:

int

type

Type of trade, either long or short.

Type:

Literal[‘long’, ‘short’]

symbol

Ticker symbol of the trade.

Type:

str

entry_date

Entry date.

Type:

numpy.datetime64

exit_date

Exit date.

Type:

numpy.datetime64

entry

Entry price.

Type:

decimal.Decimal

exit

Exit price.

Type:

decimal.Decimal

shares

Number of shares.

Type:

decimal.Decimal

pnl

Profit and loss (PnL).

Type:

decimal.Decimal

return_pct

Return measured in percentage.

Type:

decimal.Decimal

agg_pnl

Aggregate profit and loss (PnL) of the strategy after the trade.

Type:

decimal.Decimal

bars

Number of bars the trade was held.

Type:

int

pnl_per_bar

Profit and loss (PnL) per bar held.

Type:

decimal.Decimal

stop

Type of stop that was triggered, if any.

Type:

Literal[‘bar’, ‘loss’, ‘profit’, ‘trailing’] | None

mae

Maximum adverse excursion (MAE).

Type:

decimal.Decimal

mfe

Maximum favorable excursion (MFE).

Type:

decimal.Decimal