Bollinger Bands Trading Strategy#
Trading Risk Warning
IMPORTANT: All examples should be tested using demo accounts only!
- Trading involves substantial risk of loss
- These examples are for educational purposes only
- Always test with fake money before using real funds
Overview#
Bollinger Bands are a versatile technical indicator created by John Bollinger that consist of three lines:
Component | Description |
---|---|
Middle Band | A simple moving average (SMA) of the price |
Upper Band | The middle band plus a specific number of standard deviations (typically 2) |
Lower Band | The middle band minus the same number of standard deviations |
This strategy uses a mean reversion approach, which assumes that when prices move significantly away from their average, they tend to return to more normal levels:
Signal Type | Description |
---|---|
Buy signal | When price breaks below the lower band (suggesting the market is oversold) |
Sell signal | When price breaks above the upper band (suggesting the market is overbought) |
Strategy Logic#
Step | Description |
---|---|
1 | Calculate the Bollinger Bands (middle, upper, and lower bands) |
2 | Generate buy signals when price falls below the lower band |
3 | Generate sell signals when price rises above the upper band |
4 | Execute trades only during specified trading hours |
Strategy Flow#
flowchart TD
A[Start] --> B[Fetch Market Data]
B --> C[Calculate Simple Moving Average]
C --> D[Calculate Price Standard Deviation]
D --> E["Calculate Upper Band:<br/>SMA + 2×StdDev"]
D --> F["Calculate Lower Band:<br/>SMA - 2×StdDev"]
E --> G{"Price > Upper<br/>Band?"}
F --> H{"Price < Lower<br/>Band?"}
G -->|Yes| I[Generate Sell Signal]
G -->|No| J[No Sell Signal]
H -->|Yes| K[Generate Buy Signal]
H -->|No| L[No Buy Signal]
I --> M{"Within Trading<br/>Hours?"}
J --> M
K --> M
L --> M
M -->|Yes| N[Execute Trade]
M -->|No| O[Skip Trade Execution]
N --> P[Update Statistics]
O --> P
P --> Q[Wait for Next Tick]
Q --> B
Code Implementation#
Let's break down the implementation step by step:
Step 1: Required Imports#
from __future__ import annotations
import logging
import numpy as np
from mqpy.rates import Rates
from mqpy.tick import Tick
from mqpy.trade import Trade
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
We import the necessary modules:
- Core MQPy modules for trading operations and data access
- numpy
for efficient calculations of means and standard deviations
- logging
for detailed tracking of the strategy's operation
Step 2: Bollinger Bands Calculation Function#
def calculate_bollinger_bands(prices: list[float], period: int = 20, num_std_dev: float = 2.0) -> tuple[float, float, float] | None:
"""Calculate Bollinger Bands (middle, upper, lower)."""
if len(prices) < period:
return None
# Convert to numpy array for vectorized calculations
price_array = np.array(prices[-period:])
# Calculate SMA (middle band)
sma = np.mean(price_array)
# Calculate standard deviation
std_dev = np.std(price_array)
# Calculate upper and lower bands
upper_band = sma + (num_std_dev * std_dev)
lower_band = sma - (num_std_dev * std_dev)
return (sma, upper_band, lower_band)
This function calculates the three components of the Bollinger Bands:
1. First, it checks if we have enough price data for the specified period
2. It converts the price data into a numpy array for more efficient calculations
3. It calculates the middle band, which is just the simple moving average (SMA) of the prices
4. It calculates the standard deviation of the prices over the specified period
5. It calculates the upper and lower bands by adding/subtracting the standard deviation (multiplied by a factor) from the middle band
6. It returns all three values as a tuple, or None
if there's not enough data
Step 3: Initialize the Trading Strategy#
trade = Trade(
expert_name="Bollinger Bands Strategy",
version="1.0",
symbol="EURUSD",
magic_number=569,
lot=0.1,
stop_loss=50,
emergency_stop_loss=150,
take_profit=100,
emergency_take_profit=300,
start_time="9:15",
finishing_time="17:30",
ending_time="17:50",
fee=0.5,
)
We configure our trading strategy with: - Identification parameters: name, version, magic number - Trading parameters: symbol, lot size - Risk management parameters: stop loss and take profit (notice the take profit is 2x the stop loss) - Trading session times: when to start, when to stop opening new positions, and when to close all positions
Step 4: Set Strategy Parameters#
The key parameters for our Bollinger Bands strategy are:
- bb_period
: The period for the calculation of the SMA and standard deviation (standard is 20)
- bb_std_dev
: The number of standard deviations for the bands (standard is 2.0)
Step 5: Main Trading Loop#
try:
while True:
# Prepare the symbol for trading
trade.prepare_symbol()
# Fetch tick and rates data
current_tick = Tick(trade.symbol)
historical_rates = Rates(trade.symbol, bb_period + 10, 0, 1) # Get extra data for reliability
In the main loop, we: - Prepare the symbol for trading - Get the current market price - Retrieve historical price data (we get bb_period + 10 bars for reliable calculations)
Step 6: Calculate Bollinger Bands#
# Only process if we have a new tick
if current_tick.time_msc != prev_tick_time and len(historical_rates.close) >= bb_period:
# Calculate Bollinger Bands
bb_result = calculate_bollinger_bands(
historical_rates.close,
period=bb_period,
num_std_dev=bb_std_dev
)
if bb_result:
middle_band, upper_band, lower_band = bb_result
current_price = current_tick.last
For each new tick, we: - Check that it's different from the previous tick to avoid redundant calculations - Ensure we have enough historical data - Calculate the Bollinger Bands using our custom function - Extract the individual band values and the current price for signal generation
Step 7: Generate Trading Signals#
# Generate signals based on price position relative to bands
# Buy when price crosses below lower band (potential bounce)
is_buy_signal = current_price < lower_band
# Sell when price crosses above upper band (potential reversal)
is_sell_signal = current_price > upper_band
# Log band data and signals
logger.info(f"Current price: {current_price:.5f}")
logger.info(f"Bollinger Bands - Middle: {middle_band:.5f}, Upper: {upper_band:.5f}, Lower: {lower_band:.5f}")
if is_buy_signal:
logger.info(f"Buy signal: Price ({current_price:.5f}) below lower band ({lower_band:.5f})")
elif is_sell_signal:
logger.info(f"Sell signal: Price ({current_price:.5f}) above upper band ({upper_band:.5f})")
The signal generation logic is based on price comparison with the bands: 1. We generate a buy signal when the current price falls below the lower band 2. We generate a sell signal when the current price rises above the upper band 3. We log the current values of price and bands, as well as any signals generated
Step 8: Execute Trades#
# Execute trading positions based on signals
if trade.trading_time(): # Only trade during allowed hours
trade.open_position(
should_buy=is_buy_signal,
should_sell=is_sell_signal,
comment="Bollinger Bands Strategy"
)
When a signal is detected: - We check if we're within the allowed trading hours - If yes, we execute the appropriate trade based on our signals - The comment identifies the strategy in the trading terminal
Step 9: Update State and Check for End of Day#
# Update trading statistics
trade.statistics()
prev_tick_time = current_tick.time_msc
# Check if it's the end of the trading day
if trade.days_end():
trade.close_position("End of the trading day reached.")
break
After processing each tick, we: - Update the trading statistics for monitoring - Store the current tick time for the next iteration - Check if it's the end of the trading day, and if so, close positions and exit
Step 10: Error Handling#
except KeyboardInterrupt:
logger.info("Strategy execution interrupted by user.")
trade.close_position("User interrupted the strategy.")
except Exception as e:
logger.error(f"Error in strategy execution: {e}")
finally:
logger.info("Finishing the program.")
Our error handling ensures:
- Clean exit when the user interrupts the program
- Logging of any errors that occur
- Proper cleanup in the finally
block
Full Source Code#
You can find the complete source code for this strategy in the MQPy GitHub repository.
Optimization Opportunities#
This strategy can be improved by:
Improvement | Description |
---|---|
Trend Filter | Using a longer-term moving average to only take trades in the direction of the overall trend |
Band Width Analysis | Trading based on the width of the bands (narrowing and widening) to identify volatility changes |
Band Touch Strategy | Waiting for the price to return to the middle band after touching an outer band |
Volume Confirmation | Using volume information to confirm potential reversals |
Dynamic Deviation | Adjusting the number of standard deviations based on market volatility |
Next Steps#
Try experimenting with:
Experiment | Options |
---|---|
Period Length | Shorter periods for more signals, longer periods for fewer but stronger signals |
Standard Deviation | Higher values (2.5 or 3.0) for fewer but more reliable signals |
Indicator Combinations | Combine with momentum indicators like RSI to confirm signals |
Entry Refinement | Wait for reversal candle patterns after price breaks a band before entering |