Skip to content

trading.execution.backtester.orders.order_filler

trading.execution.backtester.orders.order_filler

OrderFiller

OrderFiller(config: BacktesterConfig, event_bus: EventBus)

Fills all orders made by accepting OrderEvent instances and publishing filled events.

Initializes ExecutionEngine.

Parameters:

Name Type Description Default
config BacktesterConfig

The BacktesterConfig to determine fill behavior from.

required
event_bus EventBus

The event bus to publish filled events to.

required
Source code in src\contango\trading\execution\backtester\orders\order_filler.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    config: BacktesterConfig,
    event_bus: EventBus
) -> None:
    """
    Initializes `ExecutionEngine`.

    Args:
        config: The `BacktesterConfig` to determine fill behavior from.
        event_bus: The event bus to publish filled events to.
    """
    self._config = config
    self._event_bus = event_bus

    self._last_market_event: MarketDataEvent | None = None
    self._last_portfolio_snapshot: PortfolioSnapshotEvent | None = None

collect_market_data

collect_market_data(market_data: MarketDataEvent) -> None

Collects & stores the current market event upon the event being published.

Source code in src\contango\trading\execution\backtester\orders\order_filler.py
70
71
72
73
74
def collect_market_data(self, market_data: MarketDataEvent) -> None:
    """
    Collects & stores the current market event upon the event being published.
    """
    self._last_market_event = market_data

collect_portfolio_snapshot

collect_portfolio_snapshot(event: PortfolioSnapshotEvent) -> None

Collects & stores the current portfolio snapshot upon the event being published.

Source code in src\contango\trading\execution\backtester\orders\order_filler.py
76
77
78
79
80
def collect_portfolio_snapshot(self, event: PortfolioSnapshotEvent) -> None:
    """
    Collects & stores the current portfolio snapshot upon the event being published.
    """
    self._last_portfolio_snapshot = event

fill_order_event

fill_order_event(order_event: OrderEvent) -> None

Publishes an accepted or rejected filled event order whenever an OrderEvent is created.

Parameters:

Name Type Description Default
order_event OrderEvent

The order event to process & create a FillOrder for.

required

Raises:

Type Description
RuntimeError

Upon an order being made before required events being injected into the OrderFiller.

Source code in src\contango\trading\execution\backtester\orders\order_filler.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def fill_order_event(self, order_event: OrderEvent) -> None:
    """
    Publishes an accepted or rejected filled event order whenever an `OrderEvent` is created.

    Args:
        order_event: The order event to process & create a `FillOrder` for.

    Raises:
        RuntimeError: Upon an order being made before required events being injected into the `OrderFiller`.
    """
    market_event = self._last_market_event
    portfolio_snapshot = self._last_portfolio_snapshot

    if market_event is None:
        raise RuntimeError("Tried to execute orders before market event was registered in the order filler.")
    if portfolio_snapshot is None:
        raise RuntimeError("Tried to execute orders before a portfolio snapshot was registered in the order filler.")

    fill_price = self._resolve_fill_price(market_event)
    price_with_slippage = self._apply_slippage(order_event, fill_price)
    commission = self._calculate_commission(order_event)

    order_cost = price_with_slippage * order_event.quantity
    total_cost = order_cost + commission

    # Any prohibited behavior (these are intentionally separated as they will likely be allowed in later updates).
    invalid_reason = self._validate_position_transition(order_event, portfolio_snapshot)
    if invalid_reason is not None:
        self._event_bus.publish(RejectedFillEvent(
            timestamp=market_event.timestamp,
            market_event=market_event,
            order_event=order_event,
            reason=invalid_reason
        ))
        return

    if total_cost > portfolio_snapshot.cash:
        self._event_bus.publish(RejectedFillEvent(
            timestamp=market_event.timestamp, 
            market_event=market_event,
            order_event=order_event,
            reason="Insufficient available cash (slippage & commission applied)"
        ))
        return

    if order_event.quantity + portfolio_snapshot.position < 0:
        self._event_bus.publish(RejectedFillEvent(
            timestamp=market_event.timestamp, 
            market_event=market_event,
            order_event=order_event,
            reason="Insufficient position"
        ))
        return

    fill_event = AcceptedFillEvent(
        timestamp=market_event.timestamp,
        market_event=market_event,
        order_event=order_event,
        fill_price=fill_price,
        total_cost=total_cost
    )

    self._event_bus.publish(fill_event)