PositionSizer#

class qf_lib.backtesting.position_sizer.position_sizer.PositionSizer(broker: Broker, data_provider: DataProvider, order_factory: OrderFactory, signals_register: SignalsRegister)[source]#

Bases: object

Converts Signal objects into sized orders.

Concrete implementations map each signal to a target portfolio weight or value, then call OrderFactory. When use_stop_losses=True, size_signals() also attaches stop orders from fraction_at_risk and last_available_price.

See SimplePositionSizer, FixedPortfolioPercentagePositionSizer, InitialRiskPositionSizer, and InitialRiskWithVolumePositionSizer.

size_signals(signals: List[Signal], use_stop_losses: bool = True, time_in_force: TimeInForce = TimeInForce.OPG, frequency: Frequency = None) List[Order][source]#

Based on the signals provided, creates a list of Orders where proper sizing has been applied

Parameters:
  • signals (List[Signal]) – list of signals, based on which the orders will be created

  • use_stop_losses (bool) – if true, for each MarketOrder generated for a signal, additionally a StopOrder will be created

  • time_in_force (TimeInForce) – time in force, which will be used to create the Orders based on the provided Signals

  • frequency (Frequency) – frequency of trading, further used to create Orders

  • details (StopOrders)

  • -------------------

  • quantity (For each Market Order a Stop Order is generated if and only if the quantity in Market Order + position)

  • close (for this ticker != 0. This means that StopOrders are not generated if the MarketOrder should completely)

  • ticker. (the position for the)

Returns:

Market orders sized by the concrete position sizer, optionally followed by stop orders.

Return type:

List[Order]

Examples

Portfolio value is 100,000, price is 100, so a full LONG targets 1,000 shares:

>>> sizer = SimplePositionSizer(broker, data_provider, order_factory, BacktestSignalsRegister())
>>> long_signal = Signal(ticker, Exposure.LONG, 0.02, 100.0, now)
>>> orders = sizer.size_signals([long_signal], use_stop_losses=False)
>>> len(orders)
1
>>> orders[0].quantity
1000.0

With use_stop_losses=True (default), a stop is added at last_available_price * (1 - fraction_at_risk) for a LONG signal:

>>> orders = sizer.size_signals([long_signal], use_stop_losses=True)
>>> len(orders)
2
>>> orders[1].execution_style.stop_price
98.0

An Exposure.OUT signal with no open position is ignored (no orders):

>>> flat_signal = Signal(ticker, Exposure.OUT, 0.02, 100.0, now)
>>> sizer.size_signals([flat_signal], use_stop_losses=False)
[]