Bull/Bear Large-Cap Stocks AI Trading Team¶
This strategy uses the same simple bull/bear pattern as the leveraged ETF demo, but applies it to familiar large-cap stocks. It is a cleaner starting point for people who want to understand the AI agent behavior before using more volatile leveraged instruments.
The researcher ranks the stocks, the bull and bear agents argue both sides, the interpreter turns the debate into account weights, and the trader rebalances to those weights. Because the symbols are recognizable, it is easier to read the trace and decide whether the agents are making sensible arguments.
How the team works¶
researcherranks the large-cap stock universe.bullargues for the strongest upside case.bearflags the biggest risk.interpreterweighs both cases and returns target weights for the account.traderis the only agent that can place orders. It reads the account once, sells names the weights dropped, leaves holdings within 2 percentage points of target alone, and never buys more than its cash.
Start with a historical backtest¶
Use Python 3.10 or later and a current LumiBot source checkout. Install it in a virtual environment and configure your model account:
python -m pip install -e .
export OPENAI_API_KEY="your-openai-key"
export AI_EXAMPLE_MODEL="openai/gpt-6-luna"
export LUMIBOT_AGENT_MAX_MODEL_CALLS="80"
Save the following complete runner as stock_team_backtest.py in the checkout:
from datetime import datetime
from lumibot.backtesting import YahooDataBacktesting
from lumibot.example_strategies.ai_trading_team_bull_bear_large_cap_stocks import (
AITradingTeamBullBearLargeCapStocksStrategy,
)
if __name__ == "__main__":
AITradingTeamBullBearLargeCapStocksStrategy.backtest(
YahooDataBacktesting,
datetime(2026, 1, 5),
datetime(2026, 1, 16),
budget=100_000,
benchmark_asset="SPY",
parameters={"universe": ["AAPL", "MSFT", "NVDA", "AMZN"]},
)
python stock_team_backtest.py
This imports the existing strategy class without invoking its broker runner. It uses Yahoo daily prices, four stocks, and daily agent decisions over January 5 to 15, 2026. Broker credentials are not required for this runner. The $100,000 budget is simulated portfolio capital, not a model-spending allowance.
The researcher, bull, bear, interpreter, and trader each run during a decision cycle, and a run can include several provider calls. Model usage may incur charges. The agent-call limit is not a dollar cap; a limit exit is incomplete. The trader is the only agent allowed to execute and owns the final risk check.
Inspect the decision summaries and generated backtest artifacts. Reconcile the selected stock, submitted orders, fills or no-action outcome, and terminal run status.
Latest run¶
The runner above was checked with openai/gpt-6-luna on high reasoning,
Yahoo daily prices, and a $100,000 simulated account from January 5 to 15,
2026. On January 5 the trader split the account across NVDA, AAPL, AMZN, and
MSFT, spending about $98,700. On later sessions it resized toward each day’s
weights and never bought and sold the same stock on the same day. Cash stayed
positive the whole run; the lowest balance was $206.
The account ended at $97,844, down 2.16%, while SPY rose about 1% over the same window. The largest drawdown was 2.5%. This is one short simulation, not a forecast, and a fresh model run can choose different weights.
Run the existing broker entry point¶
Only use this path when you intend broker-connected execution. The original module defaults to it; the separate runner above avoids changing its mode flag. For Alpaca, explicitly set the account mode and credentials:
export ALPACA_API_KEY="your-alpaca-key"
export ALPACA_API_SECRET="your-alpaca-secret"
export ALPACA_IS_PAPER=true
python -m lumibot.example_strategies.ai_trading_team_bull_bear_large_cap_stocks
Example code¶
"""Bull and bear large-cap stock 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 large-cap names the interpreter ranks, from this universe only. "
"Split the account by the interpreter weights."
)
_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 AITradingTeamBullBearLargeCapStocksStrategy(Strategy):
parameters = {
"universe": [
"AAPL",
"MSFT",
"NVDA",
"AMZN",
"META",
"GOOGL",
"TSLA",
"AVGO",
"COST",
"JPM",
"V",
"MA",
"LLY",
"UNH",
"XOM",
],
"max_position_pct": 1.0,
}
def initialize(self):
self.sleeptime = "1D"
add_agent(
self,
"researcher",
"Rank the large-cap universe from point-in-time prices and recent moves. 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 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
AITradingTeamBullBearLargeCapStocksStrategy.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 = AITradingTeamBullBearLargeCapStocksStrategy(broker=broker)
trader = Trader()
trader.add_strategy(strategy)
trader.run_all()