Bollinger Bands are a widely used volatility-based technical indicator. Developed by John Bollinger in the early 1980s¹, they combine a simple moving average (SMA) with standard deviation bands to provide a relative view of price volatility. With Alpaca's MCP Server, traders can use natural language prompts through AI interfaces to fetch historical market data, calculate Bollinger Bands, and structure backtesting workflows.
This guide covers the Bollinger Bands formula, how to retrieve historical data through Alpaca's Trading API, how to implement and backtest a mean-reversion strategy in Python, and how Alpaca's MCP Server can help structure each step of this process.
Key Takeaways
- Bollinger Bands use a simple moving average and standard deviation to create upper and lower price envelopes that adjust dynamically with market volatility.
- Alpaca's Market Data API provides historical OHLCV bar data that can be used to calculate Bollinger Bands in Python using libraries like pandas.
- Alpaca's MCP Server can enable traders to fetch market data, calculate indicators, and place orders using natural language across AI interfaces such as Claude, ChatGPT, Cursor, and VS Code.
- Backtesting a Bollinger Bands mean-reversion strategy can help traders evaluate signal logic against historical data, but results may not reflect future performance.
- Combining Bollinger Bands with a secondary indicator, such as RSI, may help reduce false signals, though no combination of indicators can fully account for all risk or guarantee accurate signals.
- Paper trading through Alpaca's Paper Trading API can help traders test strategies in a simulated environment before committing real capital.
This article is for general informational and educational purposes only. It is believed to be accurate as of the posting date but may be subject to change. All examples are for illustrative purposes only. The information presented is educational in nature and is not tailored to any individual’s financial situation. Readers are responsible for evaluating whether any strategy or approach is appropriate for their circumstances.
This article was commissioned and compensated by Alpaca. The content was developed for informational purposes and reflects an educational comparison of publicly available information. Compensation was provided for content creation and distribution.
What Are Bollinger Bands?
Bollinger Bands are a volatility-based technical indicator that consists of three lines plotted on a price chart:
- Middle Band: A simple moving average (SMA) of the closing price, typically over 20 periods.
- Upper Band: The SMA plus a specified number of standard deviations (typically 2).
- Lower Band: The SMA minus the same number of standard deviations.
Because the bands are calculated using standard deviation, they can expand during periods of high volatility and contract during periods of low volatility. This may make them more responsive to changing market conditions compared to fixed-width envelopes.
The Bollinger Bands Formula
For a 20-period SMA with 2 standard deviations:
- Middle Band = 20-period SMA of closing prices
- Upper Band = Middle Band + (2 x 20-period standard deviation)
- Lower Band = Middle Band - (2 x 20-period standard deviation)
The default parameters (20-period SMA, 2 standard deviations) are widely referenced⁷ in technical analysis, though traders may adjust these values depending on their timeframe and trading style.
How Some Traders Interpret Bollinger Bands
Some traders use Bollinger Bands to identify potential overbought and oversold conditions based on the concept of mean reversion:
- When price touches or crosses above the upper band, this may be interpreted as the asset being overbought relative to recent price action.
- When price touches or crosses below the lower band, this may be interpreted as the asset being oversold relative to recent price action.
- A Bollinger Band squeeze (when bands narrow significantly) may indicate a period of low volatility that could precede a larger price movement, though the direction of that movement is not specified by the indicator itself.
These interpretations are subjective, may not be reliable, and may not be appropriate across all market conditions.
Prerequisites and Environment Setup
Before implementing a Bollinger Bands strategy with Alpaca, you will need:
- Python 3.12 or later installed
- Alpaca’s Trading API keys (paper trading account available)
- The alpaca-py SDK and supporting libraries: pandas, numpy, matplotlib, python-dotenv
- For MCP Server usage: An MCP-compatible client such as Claude Desktop, Cursor, or VS Code
Install the required Python packages:
!uv pip install alpaca-py pandas numpy matplotlib python-dotenvStep 1: Set up Alpaca API Access Using Environment Variables
API keys can be stored outside source files to reduce accidental exposure. Use a .env file for local development and add it to .gitignore so credentials are never committed to repositories. Google Colab users can rely on the Colab Secrets feature.
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")Never share API keys in public or insecure environments. For production systems, consider using a secrets manager provided by your cloud platform.
How Alpaca's MCP Server Can Help With This Workflow
Alpaca's MCP Server is a component of the Model Context Protocol (MCP) architecture that exposes Alpaca's Trading API capabilities through a standardized interface. This can enable AI applications, code editors, and CLI tools to interact with market data, orders, and portfolio information using natural language.
For a Bollinger Bands workflow, the MCP Server can help with:
- Fetching historical bar data using the
get_stock_barstool to retrieve OHLCV data for any supported ticker and timeframe. - Calculating indicator values by asking an AI assistant to compute the SMA, standard deviation, and Bollinger Band levels from the retrieved data.
- Evaluating signal logic by prompting the assistant to identify where price crosses above or below the bands.
- Placing orders using the
place_stock_ordertool to submit paper or live orders based on the analysis. - Logging results to external tools such as Google Sheets for record-keeping, as demonstrated in Alpaca's MCP trading with Claude and Google Sheets tutorial.
Example MCP Prompts
When connected to Alpaca's MCP Server through an AI client like Claude Desktop or Cursor, traders might use prompts such as:
- "Extract historical market data for AAPL over the past 90 days using daily bar charts."
- "Calculate the 20-day Bollinger Bands based on that data and show me the latest values."
- "Identify days in the last 90 days where the closing price crossed below the lower Bollinger Band."
These prompts translate into structured API requests to Alpaca's Trading API. The MCP Server formats the responses so the AI tool can present market data, calculations, and potential signals in a structured format. For setup instructions, visit Alpaca's MCP Server GitHub repository.
Step 2: Get Historical Data with Alpaca's Market Data API
The first step in any Bollinger Bands implementation is to retrieve historical price data. Alpaca's Market Data API provides historical stock bars (OHLCV) across multiple timeframes.
The following snippet uses the alpaca-py SDK to request daily bar data:
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
from datetime import datetime, timedelta
# Initialize the client with your API credentials
stock_client = StockHistoricalDataClient(ALPACA_API_KEY, ALPACA_SECRET_KEY)
# Define the request parameters
request_params = StockBarsRequest(
symbol_or_symbols="AAPL",
timeframe=TimeFrame.Day,
start=datetime.now() - timedelta(days=365),
end=datetime.now()
)
# Fetch the bars and convert to a pandas DataFrame
bars = stock_client.get_stock_bars(request_params)
df = bars.df.reset_index()This returns a DataFrame containing columns for open, high, low, close, volume, and timestamp. The close column is what we will use to calculate Bollinger Bands.
For more details on available parameters such as adjustment, feed, and timeframe options, refer to the Historical Stock Data documentation.
Step 3: Calculate Bollinger Bands
With the historical data in a pandas DataFrame, calculating Bollinger Bands requires computing a rolling SMA and rolling standard deviation.
import pandas as pd
def calculate_bollinger_bands(df, window=20, num_std=2):
"""
Adds Bollinger Band columns to a DataFrame.
Args:
df: DataFrame with a 'close' column
window: Lookback period for the SMA (default: 20)
num_std: Number of standard deviations (default: 2)
Returns:
DataFrame with added bb_middle, bb_upper, bb_lower columns
"""
df["bb_middle"] = df["close"].rolling(window=window).mean()
df["bb_std"] = df["close"].rolling(window=window).std()
df["bb_upper"] = df["bb_middle"] + (num_std * df["bb_std"])
df["bb_lower"] = df["bb_middle"] - (num_std * df["bb_std"])
return df
df = calculate_bollinger_bands(df)This function:
- Computes a 20-period rolling mean of closing prices (the middle band).
- Computes a 20-period rolling standard deviation.
- Derives the upper band (SMA + 2 * std) and lower band (SMA - 2 * std).
The first 19 rows will contain NaN values because the rolling window needs a minimum of 20 data points. Filtering these rows can help keep the analysis consistent.
df = df.dropna(subset=["bb_middle"])Alternatively, Python libraries such as TA-Lib can calculate Bollinger Bands directly.
Step 4: Define a Mean-Reversion Signal Logic
A common educational example using Bollinger Bands is a mean-reversion strategy. The logic is:
- Hypothetical entry signal: When the closing price crosses below the lower Bollinger Band, this may be interpreted as a potential buy signal (the asset may be oversold relative to recent history).
- Hypothetical exit signal: When the closing price crosses above the upper Bollinger Band, this may be interpreted as a potential sell signal (the asset may be overbought relative to recent history).
This is a simplified hypothetical example for educational purposes only. It does not account for transaction costs, slippage, liquidity constraints, or changing market regimes.
def generate_signals(df):
"""
Generates buy/sell signals based on Bollinger Band crossovers.
Buy signal (1): Close crosses below lower band
Sell signal (-1): Close crosses above upper band
Hold (0): No signal
"""
df["signal"] = 0
df.loc[df["close"] < df["bb_lower"], "signal"] = 1 # Potential buy
df.loc[df["close"] > df["bb_upper"], "signal"] = -1 # Potential sell
return df
df = generate_signals(df)This produces a signal column where:
- 1 indicates a hypothetical buy condition
- -1 indicates a hypothetical sell condition
- 0 indicates no action
Some traders may refine this logic by requiring the price to not only touch but close below the band, or by adding a confirmation filter using a secondary indicator such as RSI.
Step 5: Backtest the Strategy
Backtesting applies the signal logic to historical data to evaluate how the strategy would have performed in the past. It is important to note that past hypothetical backtest results do not guarantee future returns, and actual results may vary.
A Simple Vectorized Backtest
The following code calculates hypothetical returns based on the generated signals:
import numpy as np
def backtest_strategy(df):
"""
Runs a vectorized backtest using signal-based position tracking.
This is a simplified model that does not account for
transaction costs, slippage, partial fills, or execution delays.
"""
# Forward-fill signals to maintain position until next signal
df["position"] = df["signal"].replace(0, np.nan).ffill().fillna(0)
# Calculate daily returns of the underlying asset
df["daily_return"] = df["close"].pct_change()
# Calculate strategy returns (position from previous day * today's return)
df["strategy_return"] = df["position"].shift(1) * df["daily_return"]
# Calculate cumulative returns
df["cumulative_market"] = (1 + df["daily_return"]).cumprod()
df["cumulative_strategy"] = (1 + df["strategy_return"]).cumprod()
return df
df = backtest_strategy(df)The following backtest is a simplified, hypothetical example provided for educational purposes only. It does not reflect actual trading and omits real-world factors such as transaction costs, liquidity constraints, slippage, execution delays, and changing market conditions. Results from this example should not be relied upon as indicative of future performance.
This approach:
- Forward-fills the signal so the position is held until the opposite signal appears.
- Shifts the position by one day to help reduce look-ahead bias (today's position is based on yesterday's signal).
- Computes daily strategy returns as the product of position direction and daily asset return.
Evaluate Hypothetical Performance Metrics
def calculate_metrics(df):
"""
Calculates basic hypothetical performance metrics.
These are for educational comparison only.
"""
strategy_returns = df["strategy_return"].dropna()
total_return = df["cumulative_strategy"].iloc[-1] - 1
annualized_return = (1 + total_return) ** (252 / len(strategy_returns)) - 1
annualized_volatility = strategy_returns.std() * np.sqrt(252)
# Sharpe Ratio (assuming 0% risk-free rate for simplicity)
sharpe_ratio = annualized_return / annualized_volatility if annualized_volatility != 0 else 0
# Maximum drawdown
cumulative = (1 + strategy_returns).cumprod()
rolling_max = cumulative.cummax()
drawdown = (cumulative - rolling_max) / rolling_max
max_drawdown = drawdown.min()
num_trades = (df["signal"] != 0).sum()
return {
"Total Return": f"{total_return:.2%}",
"Annualized Return": f"{annualized_return:.2%}",
"Annualized Volatility": f"{annualized_volatility:.2%}",
"Sharpe Ratio": f"{sharpe_ratio:.2f}",
"Max Drawdown": f"{max_drawdown:.2%}",
"Number of Signals": num_trades
}
metrics = calculate_metrics(df)
for key, value in metrics.items():
print(f"{key}: {value}")These metrics (total return, Sharpe Ratio, max drawdown) are commonly used measures for evaluating hypothetical backtest results. They can be interpreted carefully, as backtests are inherently limited by the data period chosen, the assumptions made, and the absence of real execution dynamics.
For a more in-depth look at backtesting frameworks, Alpaca provides resources on backtesting with vectorbt and backtesting options strategies.
Step 6: Visualize the Results
Plotting the Bollinger Bands alongside price and signals provides a visual check of the strategy logic.
The chart and signal markers shown are based on the example’s hypothetical logic and are for illustration only. They do not represent actual trading signals or recommendations to buy or sell any security.
import matplotlib.pyplot as plt
def plot_bollinger_backtest(df, symbol="AAPL"):
"""
Plots price with Bollinger Bands and buy/sell signal markers.
"""
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10),
sharex=True,
gridspec_kw={"height_ratios": [3, 1]})
# Price and Bollinger Bands
ax1.plot(df["timestamp"], df["close"], label="Close", linewidth=1)
ax1.plot(df["timestamp"], df["bb_middle"], label="SMA (20)",
linestyle="--", linewidth=0.8)
ax1.fill_between(df["timestamp"], df["bb_upper"], df["bb_lower"],
alpha=0.15, label="Bollinger Bands")
# Signal markers
buy_signals = df[df["signal"] == 1]
sell_signals = df[df["signal"] == -1]
ax1.scatter(buy_signals["timestamp"], buy_signals["close"],
marker="^", color="green", label="Buy Signal", zorder=5)
ax1.scatter(sell_signals["timestamp"], sell_signals["close"],
marker="v", color="red", label="Sell Signal", zorder=5)
ax1.set_title(f"{symbol} - Bollinger Bands Strategy (Hypothetical)")
ax1.legend()
ax1.set_ylabel("Price ($)")
# Cumulative returns comparison
ax2.plot(df["timestamp"], df["cumulative_market"], label="Buy & Hold")
ax2.plot(df["timestamp"], df["cumulative_strategy"], label="BB Strategy")
ax2.set_ylabel("Cumulative Return")
ax2.legend()
ax2.set_xlabel("Date")
plt.tight_layout()
plt.show()
plot_bollinger_backtest(df)Visual inspection can help identify periods where the strategy generated clusters of signals (which may indicate choppy market conditions) versus periods with cleaner signal separation.
Step 7: Combine Bollinger Bands with RSI
Using Bollinger Bands in isolation may produce false signals, particularly in strongly trending markets where price can remain near the upper or lower band for extended periods. Some traders add a secondary indicator as a filter.
The Relative Strength Index (RSI) is a momentum oscillator that measures the speed and magnitude of recent price changes. It ranges from 0 to 100, with readings below 30 commonly interpreted as oversold and above 70 as overbought.
Add RSI as a Confirmation Filter
def calculate_rsi(df, period=14):
"""
Calculates the Relative Strength Index (RSI).
"""
delta = df["close"].diff()
gain = delta.where(delta > 0, 0.0)
loss = -delta.where(delta < 0, 0.0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
df["rsi"] = 100 - (100 / (1 + rs))
return df
df = calculate_rsi(df)Refined Signal Logic
def generate_combined_signals(df, rsi_lower=30, rsi_upper=70):
"""
Generates signals that require both Bollinger Band
and RSI conditions to be met.
Buy: Close < lower band AND RSI < rsi_lower
Sell: Close > upper band AND RSI > rsi_upper
"""
df["combined_signal"] = 0
df.loc[
(df["close"] < df["bb_lower"]) & (df["rsi"] < rsi_lower),
"combined_signal"
] = 1
df.loc[
(df["close"] > df["bb_upper"]) & (df["rsi"] > rsi_upper),
"combined_signal"
] = -1
return df
df = generate_combined_signals(df)This combined approach may help reduce the number of false signals in certain conditions but may also result in missed opportunities or underperformance, and does not improve reliability in all market environments.
No combination of technical indicators can fully account for all risk or guarantee accurate signals.
Step 8: Move from Backtest to Paper Trading
After evaluating a strategy through backtesting, Alpaca's Paper Trading API provides a simulated environment with real market data and $100,000 in simulated funds by default. This can help traders observe how signal logic translates to order execution without risking real capital. Paper trading is a simulation only and does not account for all factors present in live trading; performance may differ from live results.
Submit a Hypothetical Paper Order
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce
# Initialize with paper trading credentials
trade_client = TradingClient(ALPACA_API_KEY, ALPACA_SECRET_KEY, paper=True)
# Example: Submit a market buy order for 1 share
order_request = MarketOrderRequest(
symbol="AAPL",
qty=1,
side=OrderSide.BUY,
time_in_force=TimeInForce.DAY
)
order = trade_client.submit_order(order_request)
print(f"Order submitted: {order.id}, Status: {order.status}")Using the MCP Server, this same action could be prompted in natural language:
"Place a paper trading market order to buy 1 share of AAPL."The MCP Server translates this into the appropriate API call. Traders can always review and confirm orders through the Alpaca’s dashboard before or after submission. For more on transitioning from paper to live trading, see Alpaca's guide on paper trading vs. live trading.
Limitations and Risk Considerations
Any backtesting or algorithmic trading workflow carries inherent limitations. Traders can consider the following:
- Look-ahead bias: If signal calculations accidentally use future data, results will be unrealistically favorable. The code examples in this guide shift position by one period to help reduce this risk.
- Survivorship bias: Backtesting only on assets that have performed well (and still exist) may inflate results.
- Overfitting: Optimizing parameters (SMA length, standard deviation multiplier, RSI thresholds) to fit historical data too closely may produce strategies that do not generalize to new data. Alpaca's resources on backtesting discuss this in greater detail.
- Execution assumptions: Real orders may experience slippage, partial fills, and delays. The vectorized backtest in this guide assumes instantaneous execution at closing prices, which is an idealized assumption.
- Market regime changes: A strategy that may appear effective during range-bound conditions could underperform during strong trends, and vice versa.
- Transaction costs: Even with commission-free trading for self-directed individual cash brokerage accounts that trade U.S. listed securities through an API, other costs such as regulatory fees may still apply, and bid-ask spreads can affect real-world returns. Certain arrangements with authorized business partners may also preclude commission-free trades. Alpaca reserves the right to charge additional fees if order flow is determined to be non-retail in nature. For full details on applicable fees, refer to the Alpaca fee schedule.
All investments involve risk, including the potential loss of principal. No strategy, indicator, or tool can guarantee returns.
Frequently Asked Questions
What are the default parameters for Bollinger Bands?
The standard defaults are a 20-period simple moving average with bands set at 2 standard deviations above and below. These defaults are widely referenced by John Bollinger and in technical analysis, though some traders adjust them based on their timeframe and the asset being analyzed.
Can I use Alpaca's MCP Server to calculate Bollinger Bands without writing Python code?
Yes. When connected to an MCP-compatible AI client such as Claude Desktop or Cursor, you can prompt the assistant to fetch historical bar data and compute Bollinger Band values using natural language. The MCP Server calls Alpaca's get_stock_bars tool, and the AI assistant handles the calculation. For setup instructions, visit Alpaca's MCP Server documentation.
Does Alpaca provide a built-in Bollinger Bands indicator through the API?
Alpaca's Market Data API provides raw OHLCV bar data. Indicator calculations such as Bollinger Bands are typically performed client-side using libraries like pandas, NumPy, or TA-Lib. Through the MCP Server, an AI assistant can perform these calculations on your behalf.
How do I know if my backtest results are reliable?
Backtest results are inherently based on historical data and idealized assumptions. To help improve reliability: test across multiple time periods and market conditions, account for transaction costs, use out-of-sample data for validation, and be cautious about over-optimizing parameters. Alpaca's guide on backtesting options strategies provides additional detail on common pitfalls.
To understand more about Alpaca's Trading API capabilities, explore the following resources:
- How to Start Paper Trading with Alpaca's Trading API
- Build a Free RSI and MACD Trading Bot with ChatGPT and Alpaca
- MCP-Driven Trading Workflow with Claude, Alpaca, and Google Sheets
- Trading API 30 Common Errors Explained and Troubleshooting
- Alpaca-py GitHub Page
- Alpaca-py Documentation
- Alpaca's Trading API Reference
References
- John Bollinger's Official Bollinger Band Website, BollingerBands.com.
- Alpaca's MCP Server, Alpaca Markets.
- About Trading API, Alpaca Markets.
- About Market Data API, Alpaca Markets.
- Trade with AI Using Alpaca's MCP Server, Alpaca Markets.
- Paper Trading, Alpaca Markets.
- A Complete Explanation of Bollinger Bands, BollingerBands.com.
- Download Python, Python Software Foundation.
- Alpaca, Alpaca Markets.
- What is the Model Context Protocol (MCP)?, Anthropic.
- How to Build an MCP-Based Trading Workflow with Alpaca, Alpaca Markets, May 2025.
- alpacahq/alpaca-mcp-server, GitHub.
- Historical Stock Data, Alpaca Markets.
- Introduction to Backtesting with VectorBT, Alpaca Markets, January 2022.
- Backtesting 0DTE Options: The Complete Guide to Strategy, Setup & Performance, Alpaca Markets, August 2025.
- Paper Trading vs. Live Trading: A Data-Backed Guide on When to Start Trading Real Money, Alpaca Markets, August 2025.
- Will I Incur Commission or Clearing Fees with Alpaca?, Alpaca Markets.
- Brokerage Fee Schedule, Alpaca Markets, February 2026.
- Crypto Spot Trading, Alpaca Markets.
This article is for general informational and educational purposes only. It is believed to be accurate as of the posting date but may be subject to change. All examples are for illustrative purposes only. The information presented is educational in nature and is not tailored to any individual’s financial situation. Readers are responsible for evaluating whether any strategy or approach is appropriate for their circumstances.
This article was commissioned and compensated by Alpaca. The content was developed for informational purposes and reflects an educational comparison of publicly available information. Compensation was provided for content creation and distribution.
Nothing in this article constitutes investment, legal, or tax advice, nor does it constitute a recommendation or endorsement by Alpaca. Any third-party descriptions, comparisons, 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.
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.
Past hypothetical backtest results do not guarantee future returns, and actual results may vary from the analysis.
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.
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.
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.