Skip to content
All library documents

Timed Midpoint-Chasing Smart Limit Orders and Backtest Fills

Article Lumibot

Summary

SMART_LIMIT is a limit order approach that starts near the bid-ask midpoint and moves its price toward the relevant side of the spread on a timed schedule. Three presets vary the number of price levels and the time spent at each step. If the order remains unfilled through its final hold period, it is canceled; missing quotes trigger a market-order fallback with a warning.

For backtests, buys fill at the midpoint plus configured slippage and sells at the midpoint minus slippage. When no slippage is set on the order, strategy defaults apply, with zero as the stated default. Multi-leg orders are treated as packages and fill at the net midpoint adjusted for slippage. The document also describes run-once processing that continues handling active ladders and related broker events until completion or a bounded timeout. These are execution-model rules, not evidence that midpoint fills or the modeled ladder match live outcomes; missing quotes and bar-based data can materially change the assumed fill.

Key ideas

  • The order begins near the midpoint and advances through timed price levels toward the bid or ask.
  • Preset choices change the number of levels and the time between steps.
  • Backtest fills use midpoint pricing adjusted by side-specific slippage.
  • Missing bid or ask data causes a market-order fallback.
  • Multi-leg orders are modeled as a single package at the net midpoint plus slippage.

Tags

Full text
# smart limit


Smart Limit Orders
==================

.. meta::
   :description: SMART_LIMIT orders are midpoint-chasing limit orders that walk the bid/ask spread using a timed ladder (Option Alpha “SmartPricing” parity).

SMART_LIMIT orders are midpoint-chasing limit orders that walk the bid/ask spread
using a timed ladder (Option Alpha “SmartPricing” parity). They are meant to model
realistic execution without forcing market orders through wide option spreads.

Overview
--------

- Uses bid/ask to compute a midpoint and final price bound.
- Walks from mid toward the bid/ask using preset step timing.
- Cancels after the final hold window if unfilled.
- If bid/ask is missing, SMART_LIMIT downgrades to a market order and emits a warning.

Presets (Option Alpha parity)
-----------------------------

- **FAST**: 3 price levels, 5 seconds per step
- **NORMAL**: 4 price levels, 10 seconds per step
- **PATIENT**: 5 price levels, 20 seconds per step


Backtesting behavior
--------------------

SMART_LIMIT fills at **mid + slippage** for buys and **mid - slippage** for sells.
This matches the standard midpoint fill approximation used by platforms like
Option Alpha when only bar data is available.

If the SMART_LIMIT config does **not** specify slippage, backtests will fall back
to strategy-level defaults (``buy_trading_slippages`` / ``sell_trading_slippages``),
which accept ``TradingSlippage`` objects and default to zero when unset.

If bid/ask quotes are missing in a backtest, SMART_LIMIT downgrades to a market
order fill (next-bar open).

Trade logs for backtests include a ``trade_slippage`` column (CSV) and show slippage
in trade marker tooltips (HTML).

Usage
-----

.. code-block:: python

   from lumibot.entities import SmartLimitConfig, SmartLimitPreset

   config = SmartLimitConfig(
       preset=SmartLimitPreset.NORMAL,
       slippage=0.05,  # $0.05 from mid
   )

   order = self.create_order("SPY", 100, "buy", smart_limit=config)
   self.submit_order(order)

Multi-leg orders
----------------

SMART_LIMIT supports multi-leg orders as a package (net bid/ask/mid). In backtests,
the package fills atomically at the net midpoint plus slippage. For multi-leg SMART_LIMIT
orders, build a parent Order with ``order_class=Order.OrderClass.MULTILEG`` and provide
the child leg orders on ``child_orders``.

Scheduled and run-once execution
-------------------------------

``Trader.run_all(run_once=True)`` runs one trading iteration, then continues
advancing active SmartLimit orders until they fill or their configured final
hold ends and cancellation is confirmed by the broker. A short or zero
``LUMIBOT_SCHEDULED_POST_ITERATION_SECONDS`` does not cut off that work.

The completion drain also waits for in-flight broker submissions, pending
cancellation/replacement transitions, and queued order callbacks. Work started
by ``on_strategy_end`` finishes before the final cloud snapshot, variable backup,
and broker disconnect. Ordinary resting limit/GTC, stop and bracket orders do
not keep a run alive waiting for a fill.

The runner allows at least 300 seconds for unresolved work and extends that
budget for the longest observed SmartLimit ladder and final hold plus 300 seconds
of broker-response grace. This is a bounded total drain, not an unlimited chain
of callback-created orders. Timeout or explicit stop fails the run with visible
``drain_failed`` or ``drain_interrupted`` timing instead of claiming completion.
Arbitrary application-created background tasks are not tracked automatically,
and a library cannot prevent a host from forcibly terminating its process.

Shown in full with attribution under the source's licence. Licence: GPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.