Limit Orders and Take Profit
Limit entries, take-profit exits, expiry and stale-order hygiene.
A limit order expresses price control. It can improve execution price, but it also introduces pending state: the order may remain live, expire, or never fill.
Source
backtester/execution/order_types.py + backtester/execution/fill_engine.pyLayerOrder execution / price-controlled entry or exit
ModeOrder mode
Input
OrderType.LIMIT, side, quantity, limit price, time-in-forceOutputfill if high/low reaches the limit, or pending/expired state
Primary API
Order(..., order_type=OrderType.LIMIT, limit_price=...)Main caveatA touched limit is not exchange queue realism. It is a bar-level trigger assumption.
Fill Rules
- Buy limit fills when
bar.low <= limit_price. - Sell limit fills when
bar.high >= limit_price. - Buy theoretical fill is
min(limit_price, bar.open). - Sell theoretical fill is
max(limit_price, bar.open). - Slippage and commission are applied after the theoretical price.
Native Expiry
Use TimeInForce.DAY, expire_at or expires_after_bars instead of manually counting stale orders when the intended policy is simple.
Limit entry with three-bar expiry
python
from backtester.execution import Order, OrderSide, OrderType
self.fill_engine.submit(Order(
instrument="AAPL",
side=OrderSide.BUY,
quantity=100,
order_type=OrderType.LIMIT,
limit_price=round(bars["AAPL"].close * 0.98, 2),
expires_after_bars=3,
))DAY Order
A TimeInForce.DAY order expires after the first eligible bar if it does not fill.
Day limit order
python
self.fill_engine.submit(Order(
instrument=inst,
side=OrderSide.BUY,
quantity=shares,
order_type=OrderType.LIMIT,
limit_price=entry_limit,
time_in_force=TimeInForce.DAY,
))Take-Profit Exit
A take-profit is usually a sell limit for a long position or buy limit for a short position.
Sell take-profit
python
take_profit = round(entry_price * 1.08, 2)
self.fill_engine.submit(Order(
instrument=inst,
side=OrderSide.SELL,
quantity=pos,
order_type=OrderType.LIMIT,
limit_price=take_profit,
))Failure Modes
- Forgetting that a pending limit order can outlive the signal that created it.
- Submitting duplicate take-profit orders on every bar.
- Ignoring same-bar ambiguity when a stop and take-profit both touch.
- Assuming limit orders remove the need for slippage modeling.
- Treating a touched limit as guaranteed execution in an illiquid market.
Audit Checklist
- Which orders are still pending after each bar?
- Did the limit expire by DAY, GTD or bars-live policy?
- Did the strategy cancel stale orders when signal state changed?
- Did a gap improve the theoretical fill price?