Skip to content
All library documents

Hourly SPY VWAP Bounce Strategy with Stop-Based Position Sizing

Code Lumibot

Summary

This strategy looks for an intraday recovery after SPY falls at least 0.15% below VWAP and then closes back above it. A research agent checks the setup hourly, beginning only after 10:00, while a separate trading agent decides whether to enter. It buys only when no position is open and no trade has been made that day, places a stop just below the dip low, and sizes the trade so the stop risks at most 1% of the account. It exits at the next hourly check and before the market close.

The document provides implementation logic and a backtest/live execution switch, but no performance results or empirical validation. The hourly schedule may miss intrahour details, and the entry depends on the research agent correctly identifying the VWAP move. The stop and risk cap describe intended controls; the text does not address slippage, gaps, transaction costs, or how the strategy handles an unfilled order. It is an example strategy specification rather than evidence that the setup is profitable.

Key ideas

  • The entry setup is a dip of at least 0.15% below VWAP followed by a close back above it.
  • The strategy checks for the setup hourly and begins after 10:00.
  • It permits at most one entry per day and only when no position is held.
  • Position size is set so a stop below the dip low risks no more than 1% of account value.
  • The position is sold at the next hourly check and before the close.

Tags

Full text
# ai_vwap.py


```py
"""VWAP Strategy AI Trading Bot.

Day trades SPY around VWAP, the average price big funds watch all day. When SPY
dips below VWAP and then climbs back above it, the bot buys the bounce. A
research agent checks the price every hour. A trading agent sizes the trade from
a stop just under the dip and is out by the close.
"""

from lumibot.strategies import Strategy


class AIVWAPStrategy(Strategy):
    parameters = {"symbol": "SPY"}

    def initialize(self):
        self.sleeptime = "1H"
        self.agents.create(
            name="researcher",
            allow_trading=False,
            system_prompt=(
                "Compare the symbol's price today with its VWAP. Say whether, since the last hourly check, "
                "the price dipped at least 0.15% below VWAP and then closed back above it, and how low the "
                "dip went. Do not trade."
            ),
        )
        self.agents.create(
            name="trader",
            allow_trading=True,
            system_prompt=(
                "Day trade the symbol. If that bounce happened, we hold nothing, and we have not traded "
                "today, buy. Put a stop just under the dip's low and size the trade so the stop loses at most "
                "1% of the account. Sell at the next hourly check and always before the close."
            ),
        )

    def on_trading_iteration(self):
        if self.get_datetime().hour < 10:  # VWAP needs some of today's prices first
            return
        facts = {"symbol": self.parameters["symbol"]}
        research = self.agents["researcher"].run(task_prompt="Check the VWAP setup.", context=facts)
        self.agents["trader"].run(task_prompt="Trade the VWAP bounce.", context={**facts, "research": research.summary})


if __name__ == "__main__":
    from lumibot.credentials import IS_BACKTESTING

    if IS_BACKTESTING:
        from lumibot.backtesting import AlpacaBacktesting

        AIVWAPStrategy.backtest(AlpacaBacktesting)
    else:
        AIVWAPStrategy().run_live()

```

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.