Moving Average Crossover 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#
The Moving Average Crossover is one of the most widely used trading strategies in technical analysis. It uses two moving averages of different periods to generate trading signals:
Signal Type | Description |
---|---|
Buy signal | When the faster (shorter-period) moving average crosses above the slower (longer-period) moving average |
Sell signal | When the faster moving average crosses below the slower moving average |
This strategy aims to identify potential trend changes in the market.
Strategy Logic#
Step | Description |
---|---|
1 | Calculate two Simple Moving Averages (SMA): a short-period SMA and a long-period SMA |
2 | Compare current and previous values of both SMAs to detect crossovers |
3 | Generate buy signals when short SMA crosses above long SMA |
4 | Generate sell signals when short SMA crosses below long SMA |
5 | Execute trades only during specified trading hours |
Strategy Flow#
flowchart TD
A[Start] --> B[Fetch Market Data]
B --> C[Calculate Short-Period SMA]
B --> D[Calculate Long-Period SMA]
C --> E[Compare Current & Previous SMAs]
D --> E
E --> F{"Short MA Crossed<br/>Above Long MA?"}
F -->|Yes| G[Generate Buy Signal]
F -->|No| H{"Short MA Crossed<br/>Below Long MA?"}
H -->|Yes| I[Generate Sell Signal]
H -->|No| J[No Trading Signal]
G --> K{"Within Trading<br/>Hours?"}
I --> K
J --> K
K -->|Yes| L[Execute Trade]
K -->|No| M[Skip Trade Execution]
L --> N[Update Statistics]
M --> N
N --> O[Wait for Next Tick]
O --> B
Code Implementation#
Let's break down the implementation step by step:
Step 1: Required Imports#
from __future__ import annotations
import logging
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 from MQPy:
- Rates
: For accessing historical price data
- Tick
: For accessing current market prices
- Trade
: For executing trading operations
- logging
: For keeping track of what the strategy is doing
Step 2: Define SMA Calculation Function#
def calculate_sma(prices: list[float], period: int) -> float | None:
"""Calculate Simple Moving Average."""
if len(prices) < period:
return None
return sum(prices[-period:]) / period
This function calculates a Simple Moving Average (SMA) from a list of prices:
- It first checks if we have enough price data for the requested period
- If not, it returns None
- Otherwise, it calculates the average of the last period
prices
Step 3: Initialize the Trading Strategy#
trade = Trade(
expert_name="Moving Average Crossover",
version="1.0",
symbol="EURUSD",
magic_number=567,
lot=0.1,
stop_loss=25,
emergency_stop_loss=300,
take_profit=25,
emergency_take_profit=300,
start_time="9:15",
finishing_time="17:30",
ending_time="17:50",
fee=0.5,
)
Here we initialize the Trade
object with our strategy parameters:
- expert_name
: The name of our trading strategy
- symbol
: The trading instrument (EURUSD in this case)
- magic_number
: A unique identifier for this strategy's trades
- lot
: The trading volume
- stop_loss
/take_profit
: Risk management parameters in points
- emergency_stop_loss
/emergency_take_profit
: Larger safety values if regular ones fail
- start_time
/finishing_time
/ending_time
: Define the trading session hours
Step 4: Set Strategy Parameters#
# Strategy parameters
prev_tick_time = 0
short_period = 5
long_period = 20
# Variables to track previous state for crossover detection
prev_short_ma = None
prev_long_ma = None
We define the key parameters for our strategy:
- short_period
: The period for the fast moving average (5 bars)
- long_period
: The period for the slow moving average (20 bars)
- We also initialize variables to track the previous MA values for crossover detection
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, long_period + 10, 0, 1) # Get extra data for reliability
The main loop:
- Prepares the symbol for trading
- Gets the current market price via Tick
- Retrieves historical price data via Rates
. We request slightly more data (long_period + 10) for reliability
Step 6: Calculate Moving Averages#
# Only process if we have a new tick
if current_tick.time_msc != prev_tick_time and len(historical_rates.close) >= long_period:
# Calculate moving averages
short_ma = calculate_sma(historical_rates.close, short_period)
long_ma = calculate_sma(historical_rates.close, long_period)
For each new tick: - We check that it's different from the previous tick to avoid redundant calculations - We ensure we have enough historical data - We calculate both the short and long moving averages
Step 7: Detect Crossovers#
# Check if we have enough data for comparison
if short_ma and long_ma and prev_short_ma and prev_long_ma:
# Detect crossover (short MA crosses above long MA)
cross_above = prev_short_ma <= prev_long_ma and short_ma > long_ma
# Detect crossunder (short MA crosses below long MA)
cross_below = prev_short_ma >= prev_long_ma and short_ma < long_ma
# Log crossover events
if cross_above:
logger.info(f"Bullish crossover detected: Short MA ({short_ma:.5f}) crossed above Long MA ({long_ma:.5f})")
elif cross_below:
logger.info(f"Bearish crossover detected: Short MA ({short_ma:.5f}) crossed below Long MA ({long_ma:.5f})")
To detect crossovers, we need both current and previous MA values:
- cross_above
: Occurs when the short MA was below (or equal to) the long MA in the previous tick, but is now above it
- cross_below
: Occurs when the short MA was above (or equal to) the long MA in the previous tick, but is now below it
- We log these events for monitoring the strategy
Step 8: Execute Trades#
# Execute trading positions based on signals
if trade.trading_time(): # Only trade during allowed hours
trade.open_position(
should_buy=cross_above,
should_sell=cross_below,
comment="Moving Average Crossover Strategy"
)
When a signal is detected:
- We first check if we're within the allowed trading hours using trade.trading_time()
- If yes, we call open_position()
with our buy/sell signals
- The comment
parameter helps identify the strategy in the trading terminal
Step 9: Update State and Check End of Day#
# Update previous MA values for next comparison
prev_short_ma = short_ma
prev_long_ma = long_ma
# Update trading statistics periodically
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 previous MA values for the next iteration - We update trading statistics for monitoring - We update the previous tick time - We 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.")
Proper 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.
Backtesting and Optimization#
This strategy can be improved by:
Improvement | Description |
---|---|
Period Optimization | Finding the optimal MA periods for specific instruments |
Filter Addition | Adding filters to avoid false signals in ranging markets |
Position Sizing | Implementing dynamic position sizing based on market volatility |
Stop Management | Adding trailing stop-loss to secure profits as the trend develops |
Next Steps#
Try experimenting with different:
Experiment | Options |
---|---|
MA Periods | Try pairs like 9 and 21, or 50 and 200 for different timeframes |
MA Types | Test Exponential, Weighted, or other MA types for potential improvements |
Instruments | Apply the strategy to various forex pairs, stocks, or commodities |
Timeframes | Scale from M1 (1-minute) to D1 (daily) charts for different trading styles |