Backtesting a Forex Scalping Strategy with EMA Using Python

Forex trading strategies are essential for traders aiming to capitalize on market movements. Among these, scalping is a popular method that involves making multiple trades throughout the day to profit from small price changes. This blog post will guide you through a comprehensive Forex scalping strategy using Exponential Moving Averages (EMA) and the Average True Range (ATR) indicators. We will also backtest the strategy using Python to validate its effectiveness.

Backtesting a Forex Scalping Strategy Step-BY-Step Guide:

Fetching Historical Data

To begin with, we need to fetch historical data for the EUR/USD currency pair. Using the yfinance library, we can download 15-minute interval data for the past 60 days.

				
					import pandas as pd
import yfinance as yf

ticker = "EURUSD=X"
interval = "15m"
period = "60d"
data = yf.download(ticker, period=period, interval=interval)
data.to_csv("USDEUR.csv")

				
			

This block of code downloads the historical price data and saves it to a CSV file. This data will be used to calculate the indicators necessary for our scalping strategy.

Loading and Preparing Data

				
					data_from_csv = pd.read_csv("USDEUR.csv", index_col='Datetime', parse_dates=True)
df = data_from_csv[data_from_csv['Volume'] == 0]

				
			
Forex Scalping strategy data

Filtering out zero-volume rows ensures that we are analyzing active trading periods, which is crucial for accurate signal generation in Forex trading strategies.

Calculating EMA and ATR Indicators

We calculate the EMA for different periods (15, 30, and 50) and the ATR to understand market volatility.

				
					import pandas_ta as ta

df["EMA15"] = ta.ema(df['Close'], length=15)
df["EMA30"] = ta.ema(df['Close'], length=30)
df["EMA50"] = ta.ema(df['Close'], length=50)
df['ATR'] = ta.atr(df['High'], df['Low'], df['Close'])

				
			

These indicators help identify the trend direction and market volatility, which are crucial for making informed trading decisions.

Generating Trading Signals

We define conditions to generate buy and sell signals based on the relative positioning and slope of the EMAs.

				
					import numpy as np

df['slopeEMA15'] = df['EMA15'].diff(periods=1).rolling(window=10).mean()
df['slopeEMA30'] = df['EMA30'].diff(periods=1).rolling(window=10).mean()
df['slopeEMA50'] = df['EMA50'].diff(periods=1).rolling(window=10).mean()

conditions = [
    (df['EMA15'] < df['EMA30']) & (df['EMA30'] < df['EMA50']) & (df['slopeEMA15'] < 0) & (df['slopeEMA30'] < 0) & (df['slopeEMA50'] < 0),
    (df['EMA15'] > df['EMA30']) & (df['EMA30'] > df['EMA50']) & (df['slopeEMA15'] > 0) & (df['slopeEMA30'] > 0) & (df['slopeEMA50'] > 0)
]

choices = [1, 2]
df['EMAsignal'] = np.select(conditions, choices, default=0)

				
			

These conditions help us identify when to enter a buy or sell position based on the alignment and trend of the EMAs, which is a key aspect of any successful Forex trading strategy.

Implementing Total Signals

We further refine the signals by considering the candle’s open and close positions relative to the EMAs and setting a wick limit to filter out noise.

				
					TotSignal = [0] * len(df)
wicklimit = 2e-5

for row in range(len(df)):
    if df['EMAsignal'][row] == 1 and df['Open'][row] > df['EMA15'][row] and df.Close[row] < df.EMA15[row] and df['High'][row] - df['Open'][row] <= wicklimit:
        TotSignal[row] = 1
    if df['EMAsignal'][row] == 2 and df['Open'][row] < df['EMA15'][row] and df.Close[row] < df.EMA15[row] and df['Open'][row] - df['Low'][row] <= wicklimit:
        TotSignal[row] = 2

df['TotSignal'] = TotSignal

				
			

This refinement helps in reducing false signals and improves the accuracy of our trading strategy.

Visualizing the Strategy

Using mplfinance, we can plot the price data along with the EMAs and generated signals.

				
					import mplfinance as mpf

def pointpos(x):
    if x['TotSignal'] == 1:
        return x['High']
    elif x['TotSignal'] == 2:
        return x['Low']
    else:
        return np.nan

df['pointpos'] = df.apply(lambda row: pointpos(row), axis=1)
df.rename(columns={'Open': 'open', 'High': 'high', 'Low': 'low', 'Close': 'close', 'Volume': 'volume'}, inplace=True)

dfpl = df[1000:1200]
apdict = [
    mpf.make_addplot(dfpl['EMA15'], color='r'),
    mpf.make_addplot(dfpl['EMA30'], color='g'),
    mpf.make_addplot(dfpl['EMA50'], color='b'),
    mpf.make_addplot(dfpl['pointpos'], type='scatter', markersize=100, marker='o', color='r')
]

mpf.plot(dfpl, type='candle', addplot=apdict, volume=True, title='EUR/USD Price with EMAs and Signals', figratio=(28, 15), style='binance')

				
			
Backtesting a Forex Scalping Strategy

Visualizing the data helps us understand how well our strategy performs over a specific period, which is crucial for backtesting Forex strategies.

Simulating Trades

We then simulate the trading process, updating our capital based on the trades executed.

				
					capital = 2000
slippage = 0
signal = ""
trd = pd.DataFrame(columns=["Signal", "Entry time", "Buy Price", "Quantity", "Exit time", "Sell price", 'Points', "Profit", "Capital"])
cur_cap = capital
position_open = False

for i in range(len(df)):
    cc = df.iloc[i]
    if signal == "":
        if cc['TotSignal'] == 1 and not position_open:
            signal = "buy"
            buy_pri = cc['close'] + cc['close'] * slippage
            sl = cc['close'] - 30e-4
            tar = cc['close'] + 50e-4
            entry_time = cc.name
            quantity = round(cur_cap / cc['close'] / 25) * 25
            position_open = True
        elif cc['TotSignal'] == 2 and not position_open:
            signal = "sell"
            sell_pri = cc['close'] + cc['close'] * slippage
            sl = cc['close'] + 30e-4
            tar = cc['close'] - 50e-4
            entry_time = cc.name
            quantity = round(cur_cap / cc['close'] / 25) * 25
            position_open = True
    elif signal == "buy" and position_open:
        if cc['high'] > tar or cc['low'] < sl:
            exit_time = cc.name
            sq = tar if cc['high'] > tar else sl
            pt = sq - buy_pri
            pro = (sq - buy_pri) * quantity
            cur_cap = cur_cap + pro
            trd.loc[len(trd.index)] = [signal, entry_time, buy_pri, quantity, exit_time, sq, pt, pro, cur_cap]
            signal = ""
            position_open = False
    elif signal == "sell" and position_open:
        if cc['high'] > sl or cc['low'] < tar:
            exit_time = cc.name
            sq = sl if cc['high'] > sl else tar
            pt = sell_pri - sq
            pro = (sell_pri - sq) * quantity
            cur_cap = cur_cap + pro
            trd.loc[len(trd.index)] = [signal, entry_time, sell_pri, quantity, exit_time, sq, pt, pro, cur_cap]
            signal = ""
            position_open = False

				
			

This simulation captures the entry and exit points of trades, calculates profits or losses, and updates the capital accordingly.

All the trade data will store in trd DataFrame.

Evaluating the Strategy

Lastly, we evaluate the strategy’s performance using quantstats.

				
					import quantstats as qs

backtest = trd.set_index('Entry time')
backtest['Returns'] = backtest['Capital'].pct_change()
backtest = backtest.fillna(0)
qs.reports.full(backtest['Returns'])

				
			

Quantstats provides a comprehensive report on the trading strategy’s performance, including key metrics such as returns, drawdowns, and various performance ratios. This evaluation is crucial to understand the effectiveness of our Forex scalping strategy.

Conclusion

In conclusion, exploring a Forex scalping strategy using EMA and ATR indicators can offer valuable insights into its potential profitability. With tools like Python and libraries such as yfinance, pandas_ta, and mplfinance, you can automate data fetching, calculate indicators, generate signals, and visualize trends. This approach involves analyzing EMA alignments and market volatility to capitalize on short-term price movements effectively.

For those eager to refine their Forex trading strategies, integrating robust backtesting methodologies is crucial. This process allows you to fine-tune strategies and make well-informed decisions, which are vital in navigating the competitive Forex landscape.

By grasping these concepts and putting them into practice, you’ll be equipped to develop and backtest your own Forex trading strategy, enhancing your ability to achieve consistent profits.

You can check our Bollinger Bands strategy backtest here.