Skip to content

Execution Engine

The Execution Engine is responsible for managing the lifecycle of your orders. It acts as the bridge between your strategy’s logic and the market, handling everything from order placement and modifications to position closures and account monitoring.

Order Connectivity

Seamlessly routes both Market and Pending (Limit/Stop) orders to the simulated or real broker.

Position Control

Granular control to close specific positions, or bulk commands to flatten your entire book.

Risk Management

Native support for updating Stop Loss (SL) and Take Profit (TP) levels on active positions.

Account monitoring

Access real-time account metrics like Balance, Equity, and Margin directly from your strategy.

In PyEventBT, strategies typically produce SignalEvents, which the system converts to OrderEvents. However, you can also interact directly with the Execution Engine via the modules object.

The most common way to execute trades is by returning SignalEvent objects from your strategy. The engine handles the rest.

  1. Strategy generates Signal: You return a SignalEvent (Buy/Sell).
  2. Sizing Engine: The signal is sized (volume calculation) based on your risk rules.
  3. Risk Check: The sized signal passes through the Risk Manager for final validation.
  4. Order Creation: If approved, an OrderEvent is created.
  5. Execution: The Execution Engine places the trade.

A common pattern for intra-day strategies is to flatten the book at the end of the session.

def check_eod_closure(event, modules):
current_time = event.datetime.time()
if current_time >= time(15, 55): # 5 mins before close
# Close everything
modules.EXECUTION_ENGINE.close_all_strategy_positions()
# Cancel any resting orders
modules.EXECUTION_ENGINE.cancel_all_strategy_pending_orders()
return []

You can implement custom trailing stops by updating the SL of an existing position.

def update_trailing_stop(event, modules):
positions = modules.EXECUTION_ENGINE._get_strategy_positions()
for pos in positions:
# If long and price moved up significantly
if pos.type == 'BUY' and event.close > pos.open_price + 20 * pip:
new_sl = event.close - 10 * pip
# Only update if new SL is higher than current SL
if new_sl > pos.sl:
modules.EXECUTION_ENGINE.update_position_sl_tp(
position_ticket=pos.ticket,
new_sl=new_sl,
new_tp=pos.tp # Keep existing TP
)

You might want to cancel only specific types of orders, for example, removing all “Buy Limit” orders if the market structure changes to bearish.

# Remove all Buy Limit orders for EURUSD
modules.EXECUTION_ENGINE.cancel_all_strategy_pending_orders_by_type_and_symbol(
order_type='ORDER_TYPE_BUY_LIMIT',
symbol='EURUSD'
)

The Execution Engine is the source of truth for account metrics (Balance, Equity, Margin) as it connects directly to the broker. However, for strategies, you should access this information through the Portfolio module.

# CORRECT way to access account info in your strategy:
equity = modules.PORTFOLIO.get_total_equity()
# INCORRECT / Internal use only:
# equity = modules.EXECUTION_ENGINE._get_account_equity()

While you use the high-level API, it’s helpful to understand what happens under the hood.

  • Order Tracking: The engine maintains a mapping of internal Order IDs to Broker Ticket IDs.
  • Reconciliation: In Live mode, it constantly checks the broker state to ensure your strategy’s view of the portfolio matches reality.
  • Error Handling: If a _send_market_order fails (e.g., due to “Market Closed”), the engine captures the OrderSendResult and prevents the creation of a ‘ghost’ position in your portfolio.

Here is the complete list of methods available in the EXECUTION_ENGINE module.

Closes every open position associated with the specific strategy instance.

def close_all_strategy_positions(self) -> None

Closes all BUY positions for a specific symbol.

def close_strategy_long_positions_by_symbol(self, symbol: str) -> None

Closes all SELL positions for a specific symbol.

def close_strategy_short_positions_by_symbol(self, symbol: str) -> None

Closes a single specific position by its ticket.

def close_position(self, position_ticket: int) -> OrderSendResult

Cancels all pending orders (Limits/Stops) for the strategy.

def cancel_all_strategy_pending_orders(self) -> None

Cancels pending orders matching a specific type and symbol.

def cancel_all_strategy_pending_orders_by_type_and_symbol(
self,
order_type: str,
symbol: str
) -> None
ParameterTypeDescription
order_typestrThe order type constant (so far ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP, ORDER_TYPE_SELL_STOP).
symbolstrThe instrument symbol.

Cancels a single specific pending order by its ticket.

def cancel_pending_order(self, order_ticket: int) -> OrderSendResult

Modifies the Stop Loss (SL) and Take Profit (TP) levels of an existing position.

def update_position_sl_tp(
self,
position_ticket: int,
new_sl: float = 0.0,
new_tp: float = 0.0
) -> None

Master switches to allow or block new order execution for the strategy.

def enable_trading(self) -> None
def disable_trading(self) -> None