Using an indicator in a live strategy

The strategy on Using an indicator in a backtest runs live without changing a line. Only how you start it changes:

from lumibot.brokers import Alpaca
from lumibot.traders import Trader
from lumibot.example_indicators.indicator_strategy import CustomIndicatorStrategy

broker = Alpaca({"API_KEY": "<your-key>", "API_SECRET": "<your-secret>", "PAPER": True})
strategy = CustomIndicatorStrategy(broker=broker, parameters={"symbol": "SPY", "length": 20})

trader = Trader()
trader.add_strategy(strategy)
trader.run_all()

Any broker LumiBot supports works (see Trading Brokers Supported by LumiBot). Start with a paper account.

What happens live

  • self.indicators.get(...) reads the bars the data source has at that moment and updates the indicator with any new closed bars.

  • Values are for closed bars, so a signal does not flicker while a bar is still forming. For live values of a forming bar, use the streaming pattern on Stream an indicator live from a crypto exchange.

Running indicators outside a strategy

IndicatorRunner runs indicators with any LumiBot data source, for scripts, notebooks or a chart server:

from lumibot.indicators import IndicatorRunner

runner = IndicatorRunner(data_source, asset, timestep="minute")
rsi = runner.add("rsi", length=14)
snapshot = runner.load_history(500)            # history, all closed
values = runner.on_bar(bar, closed=False)      # live tick
values = runner.on_bar(bar, closed=True)       # bar closed

How indicators run (the execution hook)

By default an indicator runs in the same Python process as your strategy. You never need to change that. A program that runs indicators written by other people (for example a chart server) may want them to run somewhere else, such as a separate process. It registers an IndicatorExecutor:

from lumibot.indicators import IndicatorExecutor, use_indicator_executor

class MyExecutor(IndicatorExecutor):
    name = "my-executor"

    def prepare(self, indicator):
        # Return an object with the indicator's update/reset/describe
        # methods. Returning the indicator itself keeps it in-process.
        return indicator

with use_indicator_executor(MyExecutor()):
    runner.add("rsi", length=14)

IndicatorRunner and self.indicators.get hand every new indicator to the active executor. IndicatorRunner(..., executor=...) sets one for a single runner. Indicator authors change nothing. LumiBot’s tests run indicators in a child process this way and check the numbers match an in-process run exactly.

class lumibot.indicators.IndicatorExecutor

Decides where an indicator instance runs. Subclass and override prepare().

close() → None

Release anything the executor holds (processes, connections).

prepare(indicator)

Return the object callers will use for indicator.

Called once per indicator instance, before any bar is processed. The default returns indicator itself (in-process).

lumibot.indicators.set_indicator_executor(executor: IndicatorExecutor | None) → IndicatorExecutor

Use executor for indicator instances created from now on.

None restores the in-process default. Returns the previous executor so it can be put back. Instances created earlier keep the executor they were prepared with. The setting is process-wide.

lumibot.indicators.use_indicator_executor(executor: IndicatorExecutor | None)

Temporarily use executor; the previous one is restored on exit, including when an error is raised.