Monday, December 9, 2024

Trading the Bollinger Band Breakout Strategy with AAPL

 The Bollinger Band breakout strategy is a classic approach to capturing market trends and momentum using a statistical representation of price volatility. In this blog post, we implement and simulate this strategy using Python and MetaTrader 5, applying it to historical data for AAPL (Apple Inc.) from April 2024 to the present.

Overview of the Bollinger Band Breakout Strategy

Bollinger Bands consist of three lines:

  • SMA (Simple Moving Average): The middle band, representing the average price over a specific period.
  • Upper Band: Two standard deviations above the SMA.
  • Lower Band: Two standard deviations below the SMA.

The strategy uses these bands to identify breakout opportunities:

  • Buy Signal: When the price remains near the upper band for two consecutive days.
  • Sell Signal: When the price remains near the lower band for two consecutive days.
  • Exit Criteria: Exit a trade:
    • Within four days if a profit is realized.
    • After four days if a loss exceeds 50 cents.
    • Do not exit on the first day even if the loss is 50 cents or more.

Simulation Rules and Implementation

  1. Trade Setup:
    • Start with an initial balance of $10,000.
    • Each trade involves a fixed investment of $1.
  2. Entry and Exit Points:
    • Trades are placed after consecutive signals near the upper or lower band.
    • Exit trades within the defined criteria, accounting for both profit and loss conditions.
  3. Performance Tracking:
    • The program calculates and records the profit or loss of each trade.
    • It computes the final balance after simulating the entire historical dataset.

Python Implementation

Below is a concise description of the program:

  1. Data Retrieval: Historical price data for AAPL is fetched using MetaTrader 5.
  2. Bollinger Band Calculation: The bands are computed using a 20-day SMA and standard deviation.
  3. Trade Simulation: The program simulates trades based on the Bollinger Band breakout strategy, tracking performance over time.
  4. Visualization: The results are plotted to illustrate trades on the price chart.

Key Results

The program evaluates the performance of this strategy over the given period. Each trade entry and exit point, along with its profit or loss, is displayed. Here's an example of the output:

1
2
3
4
5
6
Initial Balance: 10000.00 USD
Final Balance: 10032.75 USD

Trade Results:
Buy | Entry: 185.43 | Exit: 186.89 | Profit: 1.46 USD | Entry Date: 2024-01-31 | Exit Date: 2024-02-02
Sell | Entry: 183.21 | Exit: 182.71 | Profit: 0.50 USD | Entry Date: 2024-02-10 | Exit Date: 2024-02-14

Actual result:
With the trading rules implemented, The current balance was 10046.60 USD. if I adjust the trade amount from $1 to $5, I still get 10233.00 USD!

Chart Visualization

The chart includes:

  • Price movements with Bollinger Bands (upper, lower, and middle).
  • Entry and exit points for each trade marked on the chart.

Here’s an example of what the visualization looks like:

  • Blue Line: Closing prices.
  • Green Line: Upper Band.
  • Red Line: Lower Band.
  • Orange Line: SMA (Middle Band).
  • Trade Points: Green dots for Buy entries and red dots for Sell entries, with exit points marked as crosses.



Insights

This simulation demonstrates how the Bollinger Band breakout strategy can be used to identify potential trading opportunities. While the strategy does not guarantee profits, it offers a systematic approach to trend-following and momentum trading.


Next Steps

You can further refine this strategy by:

  1. Adjusting the Bollinger Band parameters (e.g., SMA length or standard deviation multiplier).
  2. Incorporating additional indicators for confirmation.
  3. Exploring advanced position-sizing methods to optimize returns.
Here is the complete program listing:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import MetaTrader5 as mt5
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime

# Initialize and connect to MetaTrader 5
account = nnnn  # Replace with your account number
password = "xxxx"  # Replace with your password
server = "MetaQuotes-Demo"  # Replace with your server

# Simulated starting balance
initial_balance = 10000  # Starting balance in USD
trade_amount = 1  # Trade size in USD
current_balance = initial_balance

if not mt5.initialize(login=account, password=password, server=server):
    print(f"Failed to initialize MT5: {mt5.last_error()}")
    quit()

# Symbol and timeframe
SYMBOL = "AAPL"
TIMEFRAME = mt5.TIMEFRAME_D1  # Daily timeframe

# Get historical data from 2024/04/01 to present
start_date = datetime(2024, 4, 1)
end_date = datetime.now()

rates = mt5.copy_rates_range(SYMBOL, TIMEFRAME, start_date, end_date)
if rates is None:
    print(f"Failed to retrieve data for {SYMBOL}")
    mt5.shutdown()
    quit()

# Convert to DataFrame
data = pd.DataFrame(rates)
data['time'] = pd.to_datetime(data['time'], unit='s')
data.set_index('time', inplace=True)

# Calculate Bollinger Bands
window = 20  # 20-day moving average
data['SMA'] = data['close'].rolling(window=window).mean()
data['STD'] = data['close'].rolling(window=window).std()
data['Upper Band'] = data['SMA'] + 2 * data['STD']
data['Lower Band'] = data['SMA'] - 2 * data['STD']

# Identify proximity to Bollinger Bands
threshold = 0.15  # Define "near" as within 1% of the band
data['Near Upper Band'] = abs(data['close'] - data['Upper Band']) / data['Upper Band'] < threshold
data['Near Lower Band'] = abs(data['close'] - data['Lower Band']) / data['Lower Band'] < threshold

# Simulate trades
open_trade = None  # Track the current open trade
trade_results = []  # Store trade outcomes

for i in range(len(data)):
    row = data.iloc[i]
    if open_trade is None:
        # Check for entry signals
        if (
            row['Near Upper Band']
            and data.iloc[i - 1]['Near Upper Band']
        ):
            open_trade = {
                'type': 'Buy',
                'entry_price': row['close'],
                'entry_date': row.name,
                'days_held': 0
            }
        elif (
            row['Near Lower Band']
            and data.iloc[i - 1]['Near Lower Band']
        ):
            open_trade = {
                'type': 'Sell',
                'entry_price': row['close'],
                'entry_date': row.name,
                'days_held': 0
            }
    else:
        # Update trade duration
        open_trade['days_held'] += 1

        # Calculate profit/loss
        if open_trade['type'] == 'Buy':
            profit = trade_amount * ((row['close'] - open_trade['entry_price']) )
        else:  # Sell trade
            profit = trade_amount * ((open_trade['entry_price'] - row['close']))

        # Check exit conditions
        if profit > 0 and open_trade['days_held'] <= 4:
            # Exit with profit within 4 days
            current_balance += profit
            trade_results.append({
                'type': open_trade['type'],
                'entry_price': open_trade['entry_price'],
                'exit_price': row['close'],
                'profit': profit,
                'entry_date': open_trade['entry_date'],
                'exit_date': row.name
            })
            open_trade = None
        elif open_trade['days_held'] > 3 and profit <= -0.5:
            # Exit if loss exceeds 50 cents after 4 days
            current_balance += profit
            trade_results.append({
                'type': open_trade['type'],
                'entry_price': open_trade['entry_price'],
                'exit_price': row['close'],
                'profit': profit,
                'entry_date': open_trade['entry_date'],
                'exit_date': row.name
            })
            open_trade = None

# Display trade results
print(f"Initial Balance: {initial_balance:.2f} USD")
print(f"Final Balance: {current_balance:.2f} USD")
print("\nTrade Results:")
for trade in trade_results:
    print(f"{trade['type']} | Entry: {trade['entry_price']:.2f} | Exit: {trade['exit_price']:.2f} | "
          f"Profit: {trade['profit']:.2f} USD | "
          f"Entry Date: {trade['entry_date']} | Exit Date: {trade['exit_date']}")

# Plot the chart with trades
plt.figure(figsize=(15, 8))
plt.plot(data.index, data['close'], label="Close Price", color="blue")
plt.plot(data.index, data['Upper Band'], label="Upper Band", color="green", alpha=0.5)
plt.plot(data.index, data['Lower Band'], label="Lower Band", color="red", alpha=0.5)
plt.plot(data.index, data['SMA'], label="Middle Band (SMA)", color="orange")

# Plot trade entry/exit points
for trade in trade_results:
    color = "green" if trade['type'] == 'Buy' else "red"
    marker = "o" if trade['type'] == 'Buy' else "x"
    plt.scatter(trade['entry_date'], trade['entry_price'], color="green", marker="o", zorder=5)
    plt.scatter(trade['exit_date'], trade['exit_price'], color="red", marker="x", zorder=5)

# Add a single entry and exit symbol to the legend
plt.scatter([], [], color="green", marker="o", label="Entry")
plt.scatter([], [], color="red", marker="x", label="Exit")

# Plot settings
plt.title(f"Bollinger Bands Strategy Simulation with Trade Rules")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend(loc="best")
plt.grid(True)

# Display the chart
plt.show()
# Shutdown MT5
mt5.shutdown()

Monday, December 2, 2024

Python Script to Connect to MetaTrader 5, Retrieve Ticker Info, Historical Data, and Run a Trading Bot

 In this blog post, we’ll explore a Python script that connects to MetaTrader 5 (MT5), retrieves ticker information, historical data, and executes trades with a simple trading bot. This example script demonstrates the power of MT5's Python API, offering a blend of real-time trading and data analysis, which is essential for developing automated trading systems.

Prerequisites

Before we dive into the code, make sure you have the following:

  1. MetaTrader 5 Installed: MT5 should be installed on your system.
  2. MetaTrader 5 Python API (MetaTrader5 module): You can install it via pip: pip install MetaTrader5
  3. A Demo Account on MetaTrader 5: This is required for trading simulations.
  4. Pandas Library: To handle the historical data in a DataFrame.

Script Breakdown

1. Initialize the MT5 Connection

To begin with, we need to establish a connection with MetaTrader 5. The script initializes the connection using login credentials, password, and the server name. If the connection fails, the script prints the error and exits.

2. Account Information and Symbol Selection

Next, the script retrieves and prints the account balance. It then checks if the specified symbol (in this case, TSLA) is available on the selected server. If the symbol is not available, the script shuts down the connection.

3. Retrieving Current Price

The script then retrieves the current price of the symbol. This information is essential for calculating the lot size for our trade request.

4. Trade Execution

Now that we have the current price, the script calculates the lot size based on the amount we wish to invest. The symbol_info.volume_min and symbol_info.volume_step are used to ensure that the lot size conforms to the broker’s requirements. A buy order is then created and sent.

5. Retrieving Historical Data

The script retrieves historical data for the given symbol within a defined date range. The data is then converted into a Pandas DataFrame, which allows for easy manipulation and analysis.

6. Shutdown MT5 Connection

Finally, after completing the data retrieval and trade, the script shuts down the MetaTrader 5 connection.

How This Script Works

  • Initialize Connection: Establishes a connection to MT5 with your demo account credentials.
  • Retrieve Ticker Info: Fetches the current price of the TSLA stock.
  • Calculate Lot Size: Based on the current price and the desired investment, the script calculates the appropriate lot size for the trade.
  • Execute Trade: A buy order for TSLA is placed with a specified lot size and price.
  • Historical Data Retrieval: It fetches daily historical data from 2022 to the present.
  • Shutdown: Cleanly shuts down the connection to MT5.
Bonus:

1. Run MT5 Without GUI (Headless Mode)

By default, when you run MetaTrader 5, the application window opens. However, you can prevent this window from opening by using a special configuration or by running MT5 in a detached or background mode.

To run MetaTrader 5 in headless mode:

  • MetaTrader 5 on Windows: You can start MT5 in headless mode by running the terminal with specific command-line arguments. The MT5 terminal itself does not need to be visible for the API (Python) to connect and interact with it.

Steps:

  1. Launch MT5 in Headless Mode:

    • Windows: You can start the MetaTrader 5 terminal with the -silent argument in the command line to suppress the GUI. Command: terminal.exe -silent

    • Connecting from Python: Once the terminal is running in the background (in headless mode), your Python script can connect to the MetaTrader 5 application just like it would in normal mode, and it will not open the MT5 application window.

Conclusion

This Python script provides a solid foundation for interacting with MetaTrader 5. It allows you to execute trades, retrieve real-time ticker information, and analyze historical market data using the MT5 Python API. You can expand this framework to include more advanced trading strategies, risk management features, and data analysis tools, making it a versatile tool for developing automated trading systems.

By combining Python's power with MetaTrader 5's trading capabilities, you open the door to fully automated trading, where your algorithm can handle trading decisions in real time. Happy coding, and may your trades be profitable!


Here is the complete program listing:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
import MetaTrader5 as mt5
import time
import pandas as pd
from datetime import datetime

# Account details
account = nnn #your username
password = "xxx"#password
server = "MetaQuotes-Demo" #server

SYMBOL = "TSLA"
AMOUNT = 1
start_date = datetime(2022, 1, 1)  # Start date for data
end_date = datetime.now()  # End date is the current date

# Initialize MetaTrader 5
if not mt5.initialize(login=account, password=password, server=server):
    print(f"Failed to initialize MetaTrader 5: {mt5.last_error()}")
    quit()

# Check account information
account_info = mt5.account_info()
if account_info:
    print(f"Account balance: {account_info.balance} USD")
else:
    print(f"Failed to retrieve account info: {mt5.last_error()}")
    mt5.shutdown()
    quit()

# Check and select symbol
if not mt5.symbol_select(SYMBOL, True):
    print(f"Symbol {SYMBOL} not available or not selected.")
    mt5.shutdown()
    quit()

# Get symbol info
symbol_info = mt5.symbol_info(SYMBOL)
if not symbol_info:
    print(f"Failed to retrieve symbol info for {SYMBOL}")
    mt5.shutdown()
    quit()

# Get current price
tick = mt5.symbol_info_tick(SYMBOL)
if not tick:
    print(f"Failed to get price info for {SYMBOL}")
    mt5.shutdown()
    quit()

price = tick.ask

# Calculate lot size
lot_size = max(symbol_info.volume_min, round((AMOUNT / price) / symbol_info.volume_step) * symbol_info.volume_step)

# Create trade request
trade_request = {
    "action": mt5.TRADE_ACTION_DEAL,
    "symbol": SYMBOL,
    "volume": lot_size,
    "type": mt5.ORDER_TYPE_BUY,
    "price": price,
    "deviation": 20,
#    "magic": 123456,
    "comment": "Buy TSLA $500 trade",
    "type_time": mt5.ORDER_TIME_GTC,
    "type_filling": mt5.ORDER_FILLING_FOK,  # Changed to IOC
}

# Optional: Wait for 1 second before sending the order
time.sleep(1)

# Send trade request
result = mt5.order_send(trade_request)

# Check trade result
if result.retcode == mt5.TRADE_RETCODE_DONE:
    print(f"Trade executed successfully. Ticket={result.order}")
else:
    print(f"Trade execution failed. Retcode={result.retcode}")
    print(f"Error details: {mt5.last_error()}")


# Retrieve historical data for the specified date range
rates = mt5.copy_rates_range(SYMBOL, mt5.TIMEFRAME_D1, start_date, end_date)

# Check if data was retrieved
if rates is None:
    print(f"Failed to retrieve data for {symbol}")
    mt5.shutdown()
    quit()

# Convert the data to a pandas DataFrame
data = pd.DataFrame(rates)

# Convert timestamps to datetime
data['time'] = pd.to_datetime(data['time'], unit='s')

# Set the time column as the index
data.set_index('time', inplace=True)

# Print the first few rows of the data
print(data.head())
# Shutdown MT5
mt5.shutdown()




Managing Trading Risks by Knowing Calendar Events That Affect Market Volatility

  Introduction to Trading Risks In Forex and other financial markets, risk management is one of the most critical aspects of successful trad...