Bull/Bear Leveraged ETF AI Trading Team

AI trading team workflow for bull/bear leveraged ETFs

This is a fast, dramatic AI trading team demo. It gives the agents a universe of leveraged long and inverse ETFs, turns a bull and bear debate into account weights, and asks a dedicated trading-and-risk agent to rebalance to them. The purpose is to show the full loop clearly: research, upside case, risk challenge, risk-controlled execution.

Because the universe includes both bull and bear instruments, the team can choose risk-on or risk-off exposure. That makes the decision trail easy to audit: you can inspect why the agents liked a sector, why the bear agent objected, and why the final trader still bought or sold.

How the team works

  • researcher ranks the leveraged ETF universe.

  • bull argues for the strongest money-making trade.

  • bear points out the biggest risk.

  • interpreter weighs both cases and returns target weights for the account.

  • trader is the only agent that can place orders. It holds one direction per index (never TQQQ with SQQQ, or UPRO with SPXU), sells what the weights dropped, and never buys more than its cash.

  • The universe pairs leveraged funds that move more than the index, such as TQQQ against SQQQ and UPRO against SPXU.

Latest run

A check with openai/gpt-6-luna on high reasoning, Yahoo daily prices, a $100,000 simulated account, and a TQQQ, SQQQ, UPRO, and SPXU universe ran from January 5 to 15, 2026. It bought 841 UPRO on the first session, later split the book between UPRO and TQQQ, and never held an ETF with its inverse. Cash stayed positive the whole run; the lowest balance was $189. The account ended at $101,466, up 1.47%. This is one short simulation, not a forecast.

Run it with a broker

The file defaults to broker-connected execution. With Alpaca, it runs in paper mode unless you set ALPACA_IS_PAPER=false.

export OPENAI_API_KEY='your-key-here'
export ALPACA_API_KEY='your-alpaca-key'
export ALPACA_API_SECRET='your-alpaca-secret'
export ALPACA_IS_PAPER=true
python lumibot/example_strategies/ai_trading_team_bull_bear_leveraged_etf.py

Backtest it

Use the same strategy class and change IS_BACKTESTING = False to IS_BACKTESTING = True in the runner:

export OPENAI_API_KEY='your-key-here'
python lumibot/example_strategies/ai_trading_team_bull_bear_leveraged_etf.py

Example code

"""Bull and bear leveraged ETF team.

Python only creates the agents and runs them. Bull and bear run together.
The interpreter reads both. The trader is the only order path.
"""

import os
from datetime import datetime

from lumibot.example_strategies.agent_cycle import add_agent, run_cycle, trader_prompt
from lumibot.strategies.strategy import Strategy

_BOOK = (
    "Own the leveraged ETFs the interpreter ranks, from this universe only. "
    "Respect the long or inverse direction. Split the account by the interpreter weights. "
    "Never hold a long ETF and its inverse on the same index at once, such as TQQQ with SQQQ or "
    "UPRO with SPXU: they cancel each other and both decay. Keep only the side with the larger "
    "weight and give it the net weight. When you switch sides on an index, sell the whole "
    "opposite side before buying, in the same session."
)
_EXIT = (
    "Sell a holding with the order tool when today's weights no longer include it, before any "
    "new buy. Keep a holding that today's weights still include and resize it only toward its "
    "new weight."
)


class AITradingTeamBullBearLeveragedETFStrategy(Strategy):
    parameters = {
        "universe": [
            "TQQQ",
            "SQQQ",
            "UPRO",
            "SPXU",
            "UDOW",
            "SDOW",
            "TNA",
            "TZA",
            "TECL",
            "TECS",
            "SOXL",
            "SOXS",
            "WEBL",
            "WEBS",
            "FAS",
            "FAZ",
            "LABU",
            "LABD",
            "ERX",
            "ERY",
            "GUSH",
            "DRIP",
            "DRN",
            "DRV",
            "TMF",
            "TMV",
            "NUGT",
            "DUST",
        ],
        "max_position_pct": 1.0,
    }

    def initialize(self):
        self.sleeptime = "1D"
        add_agent(
            self,
            "researcher",
            "Rank the leveraged ETF universe from point-in-time prices. Note which names are inverse. Do not submit orders.",
            allow_trading=False,
        )
        add_agent(
            self,
            "bull",
            "Argue the long case from the research only. Do not read the bear case. Do not submit orders.",
            allow_trading=False,
        )
        add_agent(
            self,
            "bear",
            "Argue the risk case from the research only. Do not read the bull case. Do not submit orders.",
            allow_trading=False,
        )
        add_agent(
            self,
            "interpreter",
            "Read the bull case and the bear case. Weight only symbols in the universe. Assign weights that sum near 100% of the account. Do not submit orders.",
            allow_trading=False,
        )
        add_agent(self, "trader", trader_prompt(book_rule=_BOOK, exit_rule=_EXIT), allow_trading=True)

    def on_trading_iteration(self):
        context = {
            "date": self.get_datetime().date().isoformat(),
            "universe": self.parameters["universe"],
            "max_position_pct": self.parameters["max_position_pct"],
        }
        run_cycle(
            self,
            context,
            researcher="researcher",
            bull="bull",
            bear="bear",
            interpreter="interpreter",
            trader="trader",
            research_task="Rank the ETF universe for this session.",
            bull_task="Make the bull case from the research.",
            bear_task="Make the bear case from the research.",
            interpret_task="Turn the bull case and the bear case into account weights.",
            trade_task="Apply the interpreter weights. Size from the account. Sell holdings the weights dropped before buying.",
        )


if __name__ == "__main__":
    IS_BACKTESTING = False

    if IS_BACKTESTING:
        from lumibot.backtesting import YahooDataBacktesting

        AITradingTeamBullBearLeveragedETFStrategy.backtest(
            YahooDataBacktesting,
            datetime(2026, 4, 7),
            datetime(2026, 5, 22),
        )
    else:
        from lumibot.brokers import Alpaca
        from lumibot.traders import Trader

        ALPACA_CONFIG = {
            "API_KEY": os.environ["ALPACA_API_KEY"],
            "API_SECRET": os.environ["ALPACA_API_SECRET"],
            "PAPER": os.environ.get("ALPACA_IS_PAPER", "true").lower() != "false",
        }

        broker = Alpaca(ALPACA_CONFIG)
        strategy = AITradingTeamBullBearLeveragedETFStrategy(broker=broker)

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


AITradingTeamStrategy = AITradingTeamBullBearLeveragedETFStrategy