Bull/Bear Leveraged ETF AI Trading Team¶
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¶
researcherranks the leveraged ETF universe.bullargues for the strongest money-making trade.bearpoints out the biggest risk.interpreterweighs both cases and returns target weights for the account.traderis 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