Mean reversion is a hypothesis in quantitative finance suggesting that asset prices may, over time, return toward a historical average or equilibrium level. Some traders design rule-based systems around this hypothesis, using statistical signals to assess when prices may have deviated from their historical norm.

This guide covers how to define a mean reversion signal using z-scores in Python, how to backtest that signal against historical bar data from Alpaca's Market Data API, and how Alpaca's MCP (Model Context Protocol) server can assist in building and iterating on this workflow. All code examples use Alpaca's paper trading environment and are strictly educational.


NOTE: This article is for general informational and educational purposes only. All examples are for illustrative purposes only.

This article was written and provided by a third party that received compensation for its creation. The views and opinions expressed are those of the third party, are presented for informational purposes only, and do not necessarily reflect the views of Alpaca. This content has not been independently verified by Alpaca and does not constitute investment, legal, or tax advice, or a recommendation or endorsement by Alpaca. Any third-party descriptions, comparisons, platform features, statements, or images are provided by such third parties and are not endorsed by Alpaca.

Investing and trading involve risk, including the potential loss of principal. Past performance (whether actual or backtested) is not indicative of future results. No investment strategy is guaranteed to achieve its objectives. Diversification does not ensure a profit or protect against loss. Investors should consider their investment objectives and risks carefully before investing.

Key Takeaways

  • Mean reversion strategies are based on a statistical hypothesis that prices may return toward a historical average; they carry risk and do not guarantee any outcome.
  • Z-score is a commonly used metric for measuring how far a current price has deviated from its rolling mean, scaled by rolling standard deviation.
  • Alpaca's Market Data API can provide access to historical OHLCV bar data for signal computation and backtesting.
  • Alpaca's MCP server can enable AI assistants such as Claude to interact with Alpaca's APIs through natural language, which may help accelerate strategy prototyping.
  • Backtesting involves applying strategy logic to historical data to observe hypothetical behavior; it does not predict future performance.
  • All API keys must be stored in environment variables or .env files and must never be committed to version control.
  • Position sizing, slippage, and risk management are not covered in simplified backtesting examples and can be incorporated before any live consideration.

What Is Mean Reversion?

Mean reversion is a statistical concept based on the idea that certain time series, including asset prices, may exhibit a tendency to return toward a long-run average after deviating from it. It is related to the concept of stationarity in time series analysis, where the statistical properties of a series may remain relatively consistent over time.

In trading, mean reversion strategies may:

  • Define a rolling average of a price series over a specified lookback window
  • Measure the current deviation from that average using standard deviation
  • Generate a signal when the deviation exceeds a defined threshold

It is important to note that mean reversion as a statistical property is not guaranteed to hold in live markets. Asset prices may trend for extended periods, and strategies built on mean reversion assumptions carry the risk of significant losses during trending regimes.

What Is Alpaca's MCP Server?

MCP is an open protocol that enables AI assistants to interact with external APIs and data sources through structured tool calls. Alpaca's MCP server makes Alpaca's trading and market data functionality available as tools that AI assistants (such as Claude) can call in response to natural language prompts.

For strategy development, this means an AI assistant connected to Alpaca's MCP server can:

  • Retrieve historical bar data from Alpaca's Market Data API
  • Check account positions and balances
  • Submit paper or live orders (all orders can be independently reviewed by the user on their Alpaca dashboard, per Alpaca's MCP Server documentation)
  • Query symbol information and account status

This capability may help reduce the iteration cycle when prototyping strategies, since developers can instruct an AI assistant to run data queries, return computed values, and draft code, all within a single workflow. Additional resources include a walkthrough of building options trading algorithms with Alpaca's MCP server and Cursor AI and a guide on MCP trading with Claude, Alpaca, and Google Sheets.

Prerequisites

Before working through the code examples in this guide, you will need:

  • An Alpaca Trading API account (paper trading is free and available to all Alpaca users)
  • Python 3.12 or later installed (per the alpaca-py SDK requirements)
  • The following Python packages: alpaca-py, pandas, numpy, python-dotenv
  • API keys from your Alpaca dashboard, stored securely as described in the next section

Install required packages:

pip install alpaca-py pandas numpy python-dotenv

For SDK details, refer to the Alpaca-py SDK documentation and Trading API documentation.

API Key Configuration Using Environment Variables

Storing API keys outside source files is a commonly recommended practice that can help reduce the risk of accidental exposure. Using a .env file or environment variables is one such approach. Keys must never be committed to repositories. Add .env to your .gitignore file.

The following pattern supports both local development and Google Colab environments:

import os
from dotenv import load_dotenv
import sys

if "google.colab" in sys.modules:
    # In Google Colab environment, fetch API keys from Secrets.
    # Users can set ALPACA_API_KEY and ALPACA_SECRET_KEY
    # in Colab Secrets via the sidebar.
    from google.colab import userdata
    ALPACA_API_KEY = userdata.get("ALPACA_API_KEY")
    ALPACA_SECRET_KEY = userdata.get("ALPACA_SECRET_KEY")
else:
    # Store API keys safely and never commit them to repositories.
    # Use .gitignore for .env files.
    load_dotenv()
    ALPACA_API_KEY = os.environ.get("ALPACA_API_KEY")
    ALPACA_SECRET_KEY = os.environ.get("ALPACA_SECRET_KEY")

Your .env file:

ALPACA_API_KEY="insert_key_here"
ALPACA_SECRET_KEY="insert_secret_here"

For production deployments, consider a dedicated secrets manager (such as AWS Secrets Manager or HashiCorp Vault) rather than .env files. Never share API keys in public repositories, chat messages, or documentation.

Fetch Historical Bar Data

To compute a mean reversion signal, you need historical closing price data. Alpaca's Market Data API can provide access to historical OHLCV (Open, High, Low, Close, Volume) bar data. For details on available data coverage, see the historical stock data documentation.

Initialize the Market Data Client

from alpaca.data.historical import StockHistoricalDataClient

# Initialize the client with your API credentials
data_client = StockHistoricalDataClient(
    ALPACA_API_KEY,
    ALPACA_SECRET_KEY
)

What this code does: It instantiates the StockHistoricalDataClient, which provides methods for requesting bars, quotes, and trades.

Why it exists: All Market Data API calls are authenticated. Credentials are passed here once and reused across requests.

When to use this: At the start of any workflow that requires historical price data.

Request Historical Bars

from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
from datetime import datetime, timedelta
import pandas as pd

def fetch_bars(symbol: str, lookback_days: int = 252) -> pd.DataFrame:
    """
    Fetch daily OHLCV bar data for a given symbol.

    Args:
        symbol: Ticker symbol (e.g., 'SPY')
        lookback_days: Number of calendar days to look back

    Returns:
        DataFrame indexed by timestamp with OHLCV columns
    """
    request_params = StockBarsRequest(
        symbol_or_symbols=symbol,
        timeframe=TimeFrame.Day,
        start=datetime.now() - timedelta(days=lookback_days),
        end=datetime.now() - timedelta(days=1)
    )
    bars = data_client.get_stock_bars(request_params)
    df = bars.df

    # Flatten multi-index if present
    if isinstance(df.index, pd.MultiIndex):
        df = df.xs(symbol, level="symbol")

    return df

What this code does: It requests daily bar data for a symbol over a specified lookback window and returns a flat DataFrame.

Why 252 days: A 252-day window is a commonly used convention in quantitative finance as an approximation for one trading year of daily data. It may provide a sufficient lookback period for computing rolling statistics, though the appropriate lookback period may vary depending on the asset and market conditions. Additional history can help stabilize rolling mean and standard deviation values.

When to use this: As the first step in any signal computation or backtesting pipeline that requires closing price history.

Compute the Mean Reversion Signal: Z-Score

A z-score measures how many standard deviations the current value lies from the rolling mean of a series. In the context of price-based mean reversion, it is computed as:

Z = (Price - Rolling Mean) / Rolling Standard Deviation

A z-score above a defined threshold may be interpreted by some traders as a signal that the price has deviated above its historical norm; below a threshold, it may be interpreted as a deviation below. This is a simplified interpretation and cannot be treated as a directional forecast.

Calculate the Rolling Z-Score

import numpy as np

def compute_zscore(df: pd.DataFrame, window: int = 20) -> pd.DataFrame:
    """
    Compute a rolling z-score of closing prices.

    Args:
        df: DataFrame with a 'close' column
        window: Lookback window for rolling mean and std

    Returns:
        DataFrame with added 'rolling_mean', 'rolling_std', 'zscore' columns
    """
    close = df["close"]
    df["rolling_mean"] = close.rolling(window=window).mean()
    df["rolling_std"] = close.rolling(window=window).std()

    # Z-score: deviation from rolling mean normalized by rolling std
    df["zscore"] = (close - df["rolling_mean"]) / df["rolling_std"]

    return df

What this code does: It adds three columns to the DataFrame: rolling_mean, rolling_std, and zscore. The z-score reflects where the current closing price sits relative to its recent distribution.

Why use a rolling window: Static historical statistics may not reflect recent price behavior. A rolling window can help keep the signal responsive to more recent data, though the appropriate window length may vary by asset and regime.

When to use this: After fetching historical bar data and before any signal detection or backtesting logic.

Detect Entry and Exit Conditions

Once z-scores are computed, entry and exit conditions can be defined by setting threshold values. The thresholds used here are illustrative; there is no single threshold that performs optimally across all assets or market conditions.

Signal Detection Function

def detect_mean_reversion_signal(
    df: pd.DataFrame,
    entry_threshold: float = 2.0,
    exit_threshold: float = 0.0
) -> str:
    """
    Detect a mean reversion signal based on z-score thresholds.

    Args:
        df: DataFrame with a 'zscore' column
        entry_threshold: Absolute z-score level at which a signal may be generated
        exit_threshold: Z-score level at which an exit signal may be generated

    Returns:
        Signal string: 'long_entry', 'short_entry', 'exit', or 'none'
    """
    if len(df) < 1:
        return "none"

    current_zscore = df["zscore"].iloc[-1]

    if np.isnan(current_zscore):
        return "none"

    if current_zscore <= -entry_threshold:
        return "long_entry"
    elif current_zscore >= entry_threshold:
        return "short_entry"
    elif abs(current_zscore) <= exit_threshold:
        return "exit"

    return "none"

What this code does: It returns a signal label based on whether the current z-score has crossed predefined thresholds.

Why check for NaN: Rolling calculations produce NaN values for the initial rows where there is insufficient history. Skipping these can help prevent errors during signal evaluation.

When to use this: After computing z-scores for each bar during backtesting or live strategy evaluation.

Backtest the Strategy

Backtesting applies strategy logic to historical data to observe how the rules would have behaved over a given period. Backtesting does not predict future performance and does not account for all real-world factors such as slippage, partial fills, market impact, or changing market regimes.

Construct a Hypothetical Backtest Loop

def run_backtest(
    df: pd.DataFrame,
    entry_threshold: float = 2.0,
    exit_threshold: float = 0.0,
    position_size: float = 1.0
) -> pd.DataFrame:
    """
    Hypothetical backtest of a mean reversion strategy.

    Args:
        df: DataFrame with 'close' and 'zscore' columns
        entry_threshold: Z-score level for entry signals
        exit_threshold: Z-score level for exit signals
        position_size: Hypothetical number of units per trade

    Returns:
        DataFrame with added columns for position, returns, and equity curve
    """
    df = df.copy()
    df["position"] = 0.0
    df["trade_return"] = 0.0

    current_position: float = 0.0
    entry_price: float = 0.0

    for i in range(1, len(df)):
        zscore = df["zscore"].iloc[i]
        close = df["close"].iloc[i]

        if np.isnan(zscore):
            df.at[df.index[i], "position"] = 0.0
            continue

        # Evaluate signal at each bar
        signal = detect_mean_reversion_signal(df.iloc[:i+1], entry_threshold, exit_threshold)

        if signal == "long_entry" and current_position == 0:
            current_position = position_size
            entry_price = close

        elif signal == "short_entry" and current_position == 0:
            current_position = -position_size
            entry_price = close

        elif signal == "exit" and current_position != 0:
            trade_pnl = current_position * (close - entry_price)
            df.at[df.index[i], "trade_return"] = trade_pnl
            current_position = 0.0
            entry_price = 0.0

        df.at[df.index[i], "position"] = current_position

    df["equity_curve"] = df["trade_return"].cumsum()
    return df

What this code does: It iterates over each bar in the DataFrame, evaluates the signal at that point in time, and records hypothetical position changes and trade returns. An equity curve is computed as the cumulative sum of trade returns.

Limitations of this example:

  • It does not model transaction costs, slippage, or partial fills.
  • It uses a fixed position size and does not include any risk management logic.
  • It evaluates signals using daily close prices, which may not reflect actual execution prices.
  • Lookahead bias is a risk in any backtest; care must be taken to ensure signals are computed only from data available at the time of each bar.

When to use this: During strategy research to assess hypothetical signal behavior across historical data, not as a basis for live trading decisions.

Evaluate Backtest Results

def summarize_backtest(df: pd.DataFrame) -> dict:
    """
    Compute basic summary statistics from a backtest result DataFrame.

    Args:
        df: DataFrame with 'trade_return' and 'equity_curve' columns

    Returns:
        Dictionary of summary metrics
    """
    trades = df[df["trade_return"] != 0]["trade_return"]

    total_trades: int = len(trades)
    total_return: float = df["equity_curve"].iloc[-1] if not df.empty else 0.0
    win_rate: float = (trades > 0).sum() / total_trades if total_trades > 0 else 0.0
    avg_trade: float = trades.mean() if total_trades > 0 else 0.0
    max_drawdown: float = (
        df["equity_curve"] - df["equity_curve"].cummax()
    ).min()

    return {
        "total_trades": total_trades,
        "total_hypothetical_return": round(total_return, 4),
        "win_rate": round(win_rate, 4),
        "avg_trade_return": round(avg_trade, 4),
        "max_drawdown": round(max_drawdown, 4)
    }

What this code does: It computes trade count, a hypothetical total return, win rate, average trade return, and maximum drawdown from the backtest result DataFrame.

Why include max drawdown: Maximum drawdown reflects the largest peak-to-trough decline in the equity curve during the backtest period. It is one indicator of the risk profile of a strategy's historical behavior, though it does not predict future drawdowns.

When to use this: After running the backtest loop to obtain a summary of hypothetical performance metrics.

Use Alpaca's MCP Server in the Strategy Workflow

Alpaca's MCP server may help accelerate the development cycle for strategies like the one described above. When an AI assistant (such as Claude) is connected to Alpaca's MCP server, it can respond to natural language prompts by calling Alpaca APIs directly.

Potential applications in a mean reversion workflow may include:

  • Data retrieval: Asking the AI to fetch bar data for a specified symbol and date range directly from Alpaca's Market Data API
  • Signal inspection: Asking the AI to compute z-scores or describe current deviation levels for a symbol
  • Position review: Asking the AI to report current open positions and account balance before considering a trade
  • Order submission: Instructing the AI to submit a paper trade when a threshold condition is met (all orders can be independently reviewed by the user on their Alpaca dashboard)

This may help reduce the number of manual API calls during prototyping. It does not replace systematic backtesting or risk management processes, and all outputs from AI-assisted workflows can be reviewed carefully before any live consideration.

Submit Orders via Alpaca's Trading API

When a signal condition is detected during operation, Alpaca's Trading API can be used to submit orders programmatically. All examples below use Alpaca's paper trading environment.

Initialize the Trading Client

from alpaca.trading.client import TradingClient

# Initialize with paper=True for the paper trading environment
trading_client = TradingClient(
    ALPACA_API_KEY,
    ALPACA_SECRET_KEY,
    paper=True
)

What this code does: It instantiates the TradingClient pointing to Alpaca's paper trading endpoint.

Why use paper=True: Paper trading is a real-time simulation environment where you can test your code using real-time market data, without involving real capital. Paper trading results are hypothetical and may not reflect the conditions of live trading. According to Alpaca's paper trading documentation, paper trading provides an approximation for what one might expect in real trading, but it is not a substitute for real trading and performance may differ.

Submit a Market Order

from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce

def submit_market_order(
    symbol: str,
    qty: int,
    side: OrderSide
) -> object:
    """
    Submit a market order for a given symbol and quantity.

    Args:
        symbol: Ticker symbol
        qty: Number of shares
        side: OrderSide.BUY or OrderSide.SELL

    Returns:
        Order object from the Trading API
    """
    order_data = MarketOrderRequest(
        symbol=symbol,
        qty=qty,
        side=side,
        time_in_force=TimeInForce.DAY
    )
    return trading_client.submit_order(order_data=order_data)

What this code does: It creates and submits a market order for the specified symbol, quantity, and side.

Why use market orders in examples: Market orders are a straightforward order type for illustration. In practice, limit orders or other order types may be more appropriate depending on liquidity and execution requirements. See Alpaca's order types documentation for the full range.

When to use this: When signal detection logic determines a condition has been met and an order is intended to be placed.

Check Open Positions

from typing import Optional

def get_open_position(symbol: str) -> Optional[object]:
    """
    Retrieve an open position for a given symbol, if one exists.

    Args:
        symbol: Ticker symbol

    Returns:
        Position object or None if no position exists
    """
    try:
        return trading_client.get_open_position(symbol)
    except Exception:
        return None

What this code does: It queries Alpaca for an existing open position in the specified symbol and returns None if no position is found.

Why check positions: This can help prevent duplicate entries and can help determine whether an exit signal is actionable.

Assemble a Hypothetical Signal Loop

The following example combines the components above into a simplified signal evaluation loop. This is a hypothetical and educational illustration, not a production-ready system.

def run_signal_check(
    symbol: str = "SPY",
    window: int = 20,
    entry_threshold: float = 2.0,
    exit_threshold: float = 0.0,
    qty: int = 1
) -> None:
    """
    Fetch data, compute z-score, check for a mean reversion signal,
    and submit a paper trade if conditions are met.

    This is for educational purposes only.
    """
    # Fetch historical data
    df = fetch_bars(symbol=symbol, lookback_days=120)

    # Compute z-score
    df = compute_zscore(df, window=window)

    # Evaluate current signal
    signal = detect_mean_reversion_signal(df, entry_threshold, exit_threshold)
    current_position = get_open_position(symbol)

    print(f"Symbol: {symbol} | Signal: {signal} | "
          f"Current Z-Score: {df['zscore'].iloc[-1]:.4f}")

    if signal == "long_entry" and current_position is None:
        print(f"Long entry signal detected. Submitting paper buy order.")
        submit_market_order(symbol, qty, OrderSide.BUY)

    elif signal == "short_entry" and current_position is None:
        print(f"Short entry signal detected. Submitting paper sell order.")
        submit_market_order(symbol, qty, OrderSide.SELL)

    elif signal == "exit" and current_position is not None:
        held_qty = int(float(current_position.qty))
        side = OrderSide.SELL if float(current_position.qty) > 0 else OrderSide.BUY
        print(f"Exit signal detected. Submitting paper close order.")
        submit_market_order(symbol, held_qty, side)

    else:
        print(f"No actionable signal at this time.")

Limitations of this example:

  • It does not include position sizing, stop-loss, or target-exit logic.
  • It runs once per call and requires a scheduler (such as a cron job or event loop) for recurring execution.
  • Market orders may experience slippage; actual fill prices may differ from the signal evaluation price.
  • It does not account for extended hours, trading halts, or market-hours validation.

Risk Considerations

All trading strategies, including those based on mean reversion, carry risk. Some considerations specific to this approach:

  • Regime risk: Mean reversion assumptions may not hold during trending or high-volatility periods, which can lead to losses as the strategy generates signals against the prevailing direction.
  • Parameter sensitivity: Z-score thresholds and rolling window lengths can influence signal frequency and behavior. No single parameter set is suitable for all assets or time periods.
  • Backtesting limitations: Backtests apply rules to past data and do not account for slippage, changing liquidity conditions, or regime shifts. Past behavior does not indicate future results.
  • Automation risk: Automated order submission can execute rapidly without human review. Errors in signal logic or data quality may lead to unintended orders.
  • Short selling risk: Short entry signals may trigger short positions, which carry the risk of theoretically unlimited loss if prices rise significantly.
  • Data quality risk: Incorrect, delayed, or incomplete price data can lead to inaccurate z-score calculations and erroneous signals.

Frequently Asked Questions

What is a z-score in the context of a mean reversion strategy?

A z-score measures how far the current closing price deviates from its rolling mean, normalized by the rolling standard deviation. In a mean reversion framework, some traders use z-score thresholds to identify when a price may have deviated substantially from its recent historical distribution. A z-score is a statistical metric and does not predict whether a price will return toward the mean.

How can Alpaca's MCP server assist in strategy development?

Alpaca's MCP server makes Alpaca's trading and market data APIs available as tools that AI assistants can call through natural language prompts. In a strategy development workflow, this may help reduce manual API calls during prototyping, for example, asking an AI assistant to fetch bar data, compute basic statistics, or check open positions. It does not replace systematic backtesting or risk evaluation.

What are the main limitations of a simple z-score backtest?

A simple z-score backtest does not account for transaction costs, slippage, or partial fills. It applies a fixed set of rules to historical data, which may not reflect how the strategy would behave during future market conditions. It also does not include position sizing or risk management logic. Backtesting is a research tool for observing hypothetical historical behavior, not a method for forecasting future results. Alpaca's resources include additional context in the paper trading vs. live trading guide.

How can I handle API credentials when building an automated trading strategy?

API credentials can be stored in environment variables or a .env file and must never be committed to version control. The article above includes an environment variable pattern that supports both local development and Google Colab environments. For production deployments, a dedicated secrets manager (such as AWS Secrets Manager or HashiCorp Vault) may provide additional protection. Keys must not be shared in public repositories, documentation, or chat messages.


References

  1. About Market Data API, Alpaca, Accessed March 2026.
  2. How to Build an MCP-Based Trading Workflow with Claude, Alpaca's Trading API, and Google Sheets, Alpaca, May 2025.
  3. Paper Trading, Alpaca, Accessed March 2026.
  4. Vibe Coding: How to Build Options Trading Algorithms with Alpaca's MCP Server & Cursor AI, Alpaca, August 2025.
  5. Model Context Protocol, Anthropic, Accessed March 2026.
  6. Alpaca Dashboard, Alpaca, Accessed March 2026.
  7. Alpaca MCP Server, Alpaca, Accessed March 2026.
  8. Alpaca Trading API Signup, Alpaca, Accessed March 2026.
  9. alpaca-py, PyPI, Accessed March 2026.
  10. Alpaca-py SDK Getting Started, Alpaca, Accessed March 2026.
  11. Alpaca API Documentation, Alpaca, Accessed March 2026.
  12. AWS Secrets Manager, Amazon Web Services, Accessed March 2026.
  13. Vault by HashiCorp, HashiCorp, Accessed March 2026.
  14. Historical Stock Data, Alpaca, Accessed March 2026.
  15. Introduction to Backtesting with VectorBT, Alpaca, January 2022.
  16. Backtesting Your Options Trading Strategies, Alpaca, February 2024.
  17. Trading API, Alpaca, Accessed March 2026.
  18. Orders at Alpaca, Alpaca, Accessed March 2026.
  19. Building Your Algorithmic Trading Setup, Alpaca, February 2024.
  20. Risks of Automated Trading Systems, Alpaca, August 2021.
  21. Paper Trading vs. Live Trading: A Data-Backed Guide on When to Start Trading Real Money, Alpaca, August 2025.
  22. About Us, Alpaca, Accessed March 2026.

Insights generated by our MCP server and connected AI agents are for educational/ informational purposes only and should not be taken as investment advice. Alpaca does not recommend any specific securities or investment strategies. Past performance from models does not guarantee future results. Please conduct your own due diligence before making any decisions. All firms mentioned operate independently and are not liable for one another.

The Paper Trading API is offered by AlpacaDB, Inc. and does not require real money or permit a user to transact in real securities in the market. Providing use of the Paper Trading API is not an offer or solicitation to buy or sell securities, securities derivative or futures products of any kind, or any type of trading or investment advice, recommendation or strategy, given or in any manner endorsed by AlpacaDB, Inc. or any AlpacaDB, Inc. affiliate and the information made available through the Paper Trading API is not an offer or solicitation of any kind in any jurisdiction where AlpacaDB, Inc. or any AlpacaDB, Inc. affiliate (collectively, “Alpaca”) is not authorized to do business.

Investing and trading involve risk, including the potential loss of principal. Past performance (whether actual or backtested) is not indicative of future results. No investment strategy is guaranteed to achieve its objectives. Diversification does not ensure a profit or protect against loss. Investors should consider their investment objectives and risks carefully before investing.

Nothing in this article constitutes investment, legal, or tax advice, nor does it constitute a recommendation or endorsement by Alpaca. Any third-party descriptions, platform features, statements, or images are provided by such third parties, have not been independently verified by Alpaca, and do not constitute a recommendation or endorsement by Alpaca.

Options trading is not suitable for all investors due to its inherent high risk, which can potentially result in significant losses. Please read “Characteristics and Risks of Standardized Options” before investing in options.

Securities brokerage services are provided by Alpaca Securities LLC (dba "Alpaca Clearing"), member FINRA/SIPC, a wholly-owned subsidiary of AlpacaDB, Inc. Technology and services are offered by AlpacaDB, Inc.

This is not an offer, solicitation of an offer, or advice to buy or sell securities or open a brokerage account in any jurisdiction where Alpaca Securities is not registered or licensed, as applicable.