Skip to content
All library documents

Multi-Agent Allocation Across Leveraged Sector ETFs

Code Lumibot

Summary

This example organizes daily decisions across leveraged sector and broad-market ETFs using separate AI agents for technology, financials, healthcare, energy, and consumer-related groups. Each sector pod is instructed to consult recent news and macroeconomic data, rank candidates from a fixed universe, and pass its recommendations to a risk manager. A portfolio manager then chooses allocations, with the design favoring diversified exposure and multiple holdings in ordinary conditions, using lower leverage as a risk-reduction choice and inverse funds only when a hedge or bearish view is supported.

The code also specifies limits on data-tool calls and requires the manager to refresh account, position, and price information immediately before orders. It includes a backtest setup with a benchmark and trading fees, but provides no backtest results or evidence that the allocation rules are profitable. Leveraged and inverse ETFs can behave differently from their underlying indexes over time; the example's prompts and safeguards do not establish that its decisions are reliable or control losses.

Key ideas

  • Separate sector-focused agents rank leveraged ETF candidates using news and macroeconomic context.
  • A risk manager reviews concentration, drawdown, reversal, macro, and inverse-fund decay concerns.
  • The portfolio manager is instructed to diversify across several holdings and vary exposure with conviction and risk.
  • The order workflow requires current account, position, and exact-symbol price information before submitting trades.
  • The example includes backtest configuration but reports no performance results.

Tags

Full text
# ai_trading_team_citadel_sector_pods_leveraged.py


```py
"""Citadel / Surveyor-inspired sector-pod AI trading team example.

Leveraged sector ETF data-on variant. Uses LumiBot's default built-in tools,
including FRED/ALFRED macro tools when FRED_API_KEY is supplied, Alpaca News
when ALPACA_NEWS_API_KEY / ALPACA_NEWS_API_SECRET are supplied, SEC tools,
market state, account state, and order tools. The trade universe is 2x/3x
leveraged sector and broad ETFs plus SHV/cash as a rare escape hatch.
"""

import os

from lumibot.credentials import IS_BACKTESTING
from lumibot.entities import Asset, TradingFee
from lumibot.strategies.strategy import Strategy
from lumibot.traders import Trader


ORDER_READINESS_RULE = (
    "Immediately before every buy or sell order, call account_portfolio, "
    "account_positions, and market_last_price for the exact ordered symbol in "
    "this same agent run, then call orders_submit_order. LumiBot rejects blind "
    "orders with ORDER_READINESS_REQUIRED when those readiness calls are missing."
)

DATA_USAGE_RULE = (
    "Use official LumiBot built-in tools by their real names: get_fred_snapshot, "
    "get_fred_latest, get_fred_series, and list_fred_series for macro evidence; "
    "alpaca_news for recent market, sector, rates, and ETF-proxy headlines; SEC "
    "tools are available and optional when company or sector fundamentals matter. "
    "Keep tool use bounded: at most one FRED snapshot or short series request and "
    "one Alpaca News call per agent run; set Alpaca News limit <= 5; do not paginate "
    "or repeatedly re-check the same evidence. Do not rely on a custom public CSV FRED helper."
)

LEVERAGE_RULE = (
    "This is a leveraged ETF strategy. Use only symbols from the leveraged universe plus SHV/cash-like exposure. "
    "Stay biased toward diversified 3x exposure when evidence supports risk-taking; use 2x as a risk-down choice; "
    "use inverse leveraged ETFs only with explicit downside or hedge evidence. Hold at least three positions in normal conditions, "
    "and do not make a single-sector all-in bet. SHV/cash may count as one position when the model refuses every reasonable leveraged setup."
)


class AITradingTeamCitadelSectorPodsStrategy(Strategy):
    parameters = {
        "universe": [
            "TECL", "TECS", "SOXL", "SOXS", "FNGU", "FNGD", "WEBL", "WEBS",
            "FAS", "FAZ", "DPST", "WDRW", "CURE", "RXD", "LABU", "LABD",
            "ERX", "ERY", "GUSH", "DRIP", "WANT", "NEED", "RETL", "DUSL",
            "SIJ", "MATL", "UTSL", "SDP", "DRN", "DRV", "NAIL", "UPRO",
            "SPXU", "SSO", "SDS", "TQQQ", "SQQQ", "QLD", "QID", "TNA",
            "TZA", "SHV",
        ],
        "min_positions": 3,
    }

    def initialize(self):
        self.sleeptime = "1D"
        model = os.environ.get("AI_TRADING_TEAM_MODEL", "openai/gpt-6-luna")
        self.agents.create(
            name="technology_pod",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Rank leveraged technology, communications, internet, AI, and semiconductor ETFs. First call alpaca_news for TECL/SOXL/FNGU/WEBL proxy headlines and call get_fred_snapshot or get_fred_latest for rates, growth, and liquidity context. "
                "Use exact symbols from the universe only. " + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="financials_pod",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Rank leveraged financial, bank, and rate-sensitive ETFs. First call get_fred_snapshot or get_fred_latest for yield curve, credit, liquidity, and policy-rate context, then call alpaca_news for financial-sector headlines. "
                "Use exact symbols from the universe only. " + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="healthcare_pod",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Rank leveraged healthcare, biotech, and defensive-growth ETFs. Call alpaca_news for healthcare/biotech/defensive-growth headlines and use FRED tools for macro risk context. "
                "Use exact symbols from the universe only. " + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="energy_pod",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Rank leveraged energy, oil, commodity, and materials ETFs. Call alpaca_news for oil/energy/materials headlines and get_fred_snapshot/get_fred_latest for inflation, rates, dollar, and growth context. "
                "Use exact symbols from the universe only. " + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="consumer_pod",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Rank leveraged consumer discretionary, staples, retail, housing, industrials, utilities, and real estate ETFs. Call alpaca_news for consumer/housing/defensive-sector headlines and FRED tools for inflation, income, rates, and growth context. "
                "Use exact symbols from the universe only. " + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="risk_manager",
            model=model,
            allow_trading=False,
            system_prompt=(
                "Compare the pod picks. Challenge crowding, factor exposure, drawdown risk, reversal risk, macro contradictions, inverse ETF decay, and single-sector concentration. "
                "Check whether pods actually used get_fred_snapshot/get_fred_latest and alpaca_news. Do not re-call tools unless pod evidence is entirely absent; if you must, make only one short FRED call and one Alpaca News call with limit <= 5. "
                + DATA_USAGE_RULE + " " + LEVERAGE_RULE
            ),
        )
        self.agents.create(
            name="portfolio_manager",
            model=model,
            allow_trading=True,
            system_prompt=(
                "Allocate across the best leveraged ETFs from the pod process. Build a diversified portfolio of at least three positions in normal conditions, using variable weights based on pod conviction and risk-manager objections. "
                "Favor 3x ETFs for high-conviction sector sleeves, use 2x when conviction or drawdown risk is lower, and use inverse ETFs only as a carefully justified hedge or downside exposure. "
                "Do not make a one-sector all-in trade. Reconcile any override of pod advice or risk-manager warnings. "
                + DATA_USAGE_RULE + " " + LEVERAGE_RULE + " " + ORDER_READINESS_RULE
            ),
        )

    def on_trading_iteration(self):
        context = {
            "date": self.get_datetime().date().isoformat(),
            "universe": self.parameters["universe"],
            "min_positions": self.parameters["min_positions"],
            "data_expectation": "Use get_fred_snapshot/get_fred_latest and alpaca_news commonly; smoke tests inspect agent_detail for actual tool calls.",
            "leverage_expectation": "Use only leveraged ETFs plus SHV/cash escape, hold at least three positions, and avoid single-sector all-in behavior.",
            "data_tool_validation_run_id": "2026-07-08-fresh-alpaca-news-fred-smoke-v1",
        }
        technology = self.agents["technology_pod"].run(
            task_prompt="Call alpaca_news and FRED tools, then rank leveraged technology/communications/semiconductor opportunities.",
            context=context,
        )
        financials = self.agents["financials_pod"].run(
            task_prompt="Call get_fred_snapshot or get_fred_latest and alpaca_news, then rank leveraged financial/rate-sensitive opportunities.",
            context=context,
        )
        healthcare = self.agents["healthcare_pod"].run(
            task_prompt="Call alpaca_news and FRED tools, then rank leveraged healthcare/biotech/defensive-growth opportunities.",
            context=context,
        )
        energy = self.agents["energy_pod"].run(
            task_prompt="Call alpaca_news and FRED tools, then rank leveraged energy/commodity/materials opportunities.",
            context=context,
        )
        consumer = self.agents["consumer_pod"].run(
            task_prompt="Call alpaca_news and FRED tools, then rank leveraged consumer/housing/defensive/real-estate opportunities.",
            context=context,
        )
        risk = self.agents["risk_manager"].run(
            task_prompt="Compare all pod picks, challenge concentration/crowding/macro/inverse-decay risks, and recommend a diversified 3+ leveraged ETF allocation.",
            context={**context, "technology": technology.summary, "financials": financials.summary, "healthcare": healthcare.summary, "energy": energy.summary, "consumer": consumer.summary},
        )
        self.agents["portfolio_manager"].run(
            task_prompt=(
                "Rebalance into the best diversified 3+ leveraged ETF portfolio. Bias toward 3x ETFs, use 2x only as a risk-down choice, and use inverse ETFs only with explicit hedge/downside evidence. "
                "Do not sell everything into one strongest ETF. Before each order, call account_portfolio, account_positions, and market_last_price for the exact ordered symbol in this same run. "
                "Explain which pod advice you accepted or rejected and cite FRED/Alpaca evidence used."
            ),
            context={**context, "technology": technology.summary, "financials": financials.summary, "healthcare": healthcare.summary, "energy": energy.summary, "consumer": consumer.summary, "risk": risk.summary},
        )


if __name__ == "__main__":
    quote_asset = Asset("USD", Asset.AssetType.FOREX)
    params = AITradingTeamCitadelSectorPodsStrategy.parameters

    if IS_BACKTESTING:
        trading_fee = TradingFee(percent_fee=0.001)
        AITradingTeamCitadelSectorPodsStrategy.backtest(
            datasource_class=None,
            benchmark_asset=Asset("SPY", Asset.AssetType.STOCK),
            buy_trading_fees=[trading_fee],
            sell_trading_fees=[trading_fee],
            quote_asset=quote_asset,
            parameters=params,
        )
    else:
        trader = Trader()
        strategy = AITradingTeamCitadelSectorPodsStrategy(
            quote_asset=quote_asset,
            parameters=params,
        )
        trader.add_strategy(strategy)
        trader.run_all()

```

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.