Backtesting Scalping Strategy with Tick Data API

Backtesting Scalping Success Using Tick Data and EODHD

Pranjal Saxena
Level Up Coding

--

Photo by Campaign Creators on Unsplash

Scalping is a trading strategy focused on profiting from minor price changes. Traders executing this strategy aim to enter and exit positions quickly. The goal is to accumulate gains that, albeit small individually, can be substantial collectively when added up over time.

This approach requires a precise understanding of market movements. It often relies on high-frequency trading tools and real-time data analysis. The rapid execution of trades, combined with a strict exit strategy, minimizes risks associated with longer exposure times.

The essence of scalping lies in its reliance on market inefficiencies and liquidity. As such, access to comprehensive and instantaneous data is crucial. This is where tick data comes into play, offering detailed insights into every price change, however minute, providing scalpers with the up-to-the-second information they need to make informed decisions.

The objective of this article is to explore the application of the EODHD API’s tick data in backtesting a scalping strategy. Backtesting is a critical step for traders to evaluate the effectiveness of a strategy without risking capital. It involves simulating a trading strategy using historical data to ascertain its potential for success.

Using the EODHD API, we will demonstrate how traders can harness tick data to test scalping strategies under various market conditions. This API offers detailed, granular data essential for a high-resolution view of market dynamics, crucial for a strategy that depends on quick, small gains.

Furthermore, the practicality of Python as a programming tool will be underscored through its application in fetching and analyzing this data. Python’s capabilities in data manipulation and analysis make it an ideal candidate for implementing complex trading algorithms quickly and efficiently.

By the end of this discussion, readers will gain an understanding of how to utilize tick data effectively and how to set up a backtesting environment using Python. This will equip them with the knowledge to test their scalping strategies rigorously, ensuring they are well-prepared before entering the high-stakes world of trading.

In the following section, we will delve deeper into the capabilities of the EODHD API. This will include a detailed look at the types of data it offers and why such data is indispensable for successful scalping strategies.

EODHD’s Tick Data API

We are going to use tick data provided by one of the financial data providers, EODHD, via their API. Tick data is available among a variety of other APIs, including end-of-day prices, fundamentals, technical indicators, and historical tick data, all in JSON and CSV formats. The data covers 60 global exchanges, and the API includes information on stocks, ETFs, mutual funds, and bonds.

Tick data is included in a few paid plans (check their Pricing page). However, here’s a tip: contact EODHD support, and they might offer a package specifically for tick data at a competitive price. Also, check their Medium page for for more updates.

Tick data, which is among the API’s offerings, is particularly vital for high-frequency trading and scalping strategies. This data type records every single transaction for a given security, providing details on price, volume, and time down to the millisecond. Such granularity allows traders to see minute-by-minute price movements, crucial for strategies that capitalize on small, short-term fluctuations.

In the realm of scalping, where profits are made on slight price changes within very short time frames, having access to real-time tick data can significantly enhance decision-making processes. Traders can react swiftly to price changes, optimizing entry and exit points to maximize gains from minor adjustments in the market.

Moreover, tick data’s comprehensive view of market activity helps in identifying trends and patterns that may not be visible through less detailed data sets. This can be instrumental in refining trading algorithms and strategies, providing a competitive edge in the fast-paced trading environment.

As we delve deeper into the utility of the EODHD API, it becomes evident that its capabilities are not just operational but strategic. The next sections will explore what tick data is in more detail and how it can be employed specifically in the context of scalping strategies. We will look at how this high-resolution data is pivotal in crafting strategies that require precision and speed.

Understanding Tick Data

Tick data represents the most granular level of information available to traders, detailing each transaction in a market. It includes the exact time, price, and quantity of every trade executed, capturing every fluctuation in the market. This high-resolution data is essential for analyzing market behavior at a microscopic level.

For scalpers and high-frequency traders, tick data is invaluable. These traders thrive on short-term movements, often holding positions for mere seconds. For such strategies, the precision and depth of tick data can reveal opportunities that aggregated data might miss. It provides a clear picture of market dynamics at any given moment, essential for making informed trading decisions swiftly.

To fetch tick data from the EODHD API, one would typically use a programming language like Python. Here’s a simple example of how we can fetch minute wise (Apple Inc. AAPL) tick data from the API:

import requests
import pandas as pd
from datetime import datetime

# Function to convert date to UNIX timestamp
def date_to_unix(date_str):
"""Converts date in 'YYYY-MM-DD' format to UNIX timestamp."""
return int(datetime.strptime(date_str, '%Y-%m-%d').timestamp())

# API Parameters
api_key = 'YOUR_EODHD_API_KEY'
symbol = 'AAPL.US'
start_date = '2024-03-25'
end_date = '2024-04-05'
start_unix = date_to_unix(start_date)
end_unix = date_to_unix(end_date)

# Construct the API URL
url = f"https://eodhd.com/api/ticks/?api_token={api_key}&s={symbol}&from={start_unix}&to={end_unix}&fmt=json"

# Make the API request
response = requests.get(url)
data = response.json()

# # Load data into a pandas DataFrame
df = pd.DataFrame(data)

# Convert UNIX timestamp to readable datetime format
df['timestamp'] = pd.to_datetime(df['ts'], unit='ms')

# Selecting only the relevant columns for backtesting
df = df[['price', 'shares', 'timestamp']]

# Indexing by timestamp to facilitate time series operations
df.set_index('timestamp', inplace=True)
print(df.head()) # Display the first few rows of the DataFrame
Tick Data From EODHD

This script makes an HTTP request to the EODHD API, retrieving tick-by-tick data for the specified symbol. The returned data includes all individual trades, which can be analyzed to detect quick shifts in market sentiment or volume bursts.

Tick data’s depth makes it perfect for backtesting scalping strategies, which rely heavily on simulating trades against historical data. Scalpers can evaluate the effectiveness of their strategies under various market conditions, fine-tuning their approaches based on very specific, time-sensitive price movements.

Moreover, using tick data for scalping allows traders to test their strategies with precision. They can assess slippage more accurately and adjust their trading algorithms to optimize entry and exit points. This level of detail helps in crafting strategies that are not only reactive but also proactive, anticipating market moves before they happen.

In the next section, we will dive into the methodology for backtesting a scalping strategy using this precise and valuable tick data. We will outline the steps needed to set up a backtesting environment that can simulate trading strategies with high fidelity, reflecting real-world trading conditions as closely as possible.

Methodology for Backtesting a Scalping Strategy

Strategy Overview

Scalping strategies are designed for fast action and small profit margins. They involve executing numerous trades to capitalize on small price changes. This strategy requires precise execution and rapid decision-making.

This article uses Bollinger Bands, MACD, and RSI in the strategy. These indicators help us determine entry and exit points on a very short-term basis. Bollinger Bands provide insights into market volatility. MACD measures momentum, and RSI identifies overbought or oversold conditions. This combination allows scalpers to make informed decisions quickly.

  • Bollinger Bands: Help gauge market volatility and identify potential price boundaries.
  • MACD (Moving Average Convergence Divergence): Assists in identifying trend reversals and momentum.
  • RSI (Relative Strength Index): Determines overbought or oversold conditions, signaling potential entry or exit points.

The core of this strategy involves specific conditions for making trades:

Buy Conditions

  • The price crosses above the lower Bollinger Band, suggesting an oversold market.
  • The MACD line crosses above the signal line, indicating increasing momentum.
  • The RSI is below 30, signaling an oversold condition.

Sell Conditions

  • The price crosses below the upper Bollinger Band, suggesting an overbought market.
  • The MACD line crosses below the signal line, indicating decreasing momentum.
  • The RSI is above 70, signaling an overbought condition.
Buying and Selling Strategy
Buy Signal Example

Testing Framework

Backtesting a strategy, especially for scalping, requires a meticulous framework. This setup tests the strategy’s efficacy using historical data. Here’s how we structure our backtesting:

Initial Setup:

  • Define the starting capital.
  • Set trading commissions.
  • Select the historical data range to cover diverse market conditions.

Backtesting Environment:

  • Utilize Backtrader, a Python library suited for testing trading algorithms against historical market data.
  • Integrate the EODHD API to pull real-time tick data, essential for the granularity needed in scalping.

Parameters Setup:

  • Timeframes for indicators are set to capture short-term market dynamics effectively.
  • Thresholds for Bollinger Bands, MACD, and RSI are defined to trigger buy or sell signals accurately.

This structured approach helps simulate the strategy in varied market environments, providing insights into potential real-world performance.

Transitioning from theory to practice, the next section, “Python Implementation,” will guide through the technical setup required to operationalize and test this strategy using Python. This will include detailed steps for setting up the environment, fetching the necessary data, and executing the strategy within a backtesting framework.

Python Implementation of Backtesting Using Tick Data

Implementing a scalping strategy using Python involves several key steps, from fetching data to running the backtest. Here’s a concise overview of each step, ensuring you can effectively replicate and understand the process using your existing code:

Data Fetch

The first step involves retrieving high-frequency tick data essential for scalping strategies. Data is fetched from the EODHD API, which provides granular tick data.

import requests
import pandas as pd
from datetime import datetime

# Function to convert date to UNIX timestamp
def date_to_unix(date_str):
"""Converts date in 'YYYY-MM-DD' format to UNIX timestamp."""
return int(datetime.strptime(date_str, '%Y-%m-%d').timestamp())

# API Parameters
api_key = 'YOUR_EODHD_API_KEY'
symbol = 'AAPL.US'
start_date = '2024-03-25'
end_date = '2024-04-05'
start_unix = date_to_unix(start_date)
end_unix = date_to_unix(end_date)

# Construct the API URL
url = f"https://eodhd.com/api/ticks/?api_token={api_key}&s={symbol}&from={start_unix}&to={end_unix}&fmt=json"

# Make the API request
response = requests.get(url)
data = response.json()

# # Load data into a pandas DataFrame
df = pd.DataFrame(data)

# Convert UNIX timestamp to readable datetime format
df['timestamp'] = pd.to_datetime(df['ts'], unit='ms')

Data Preparation

Once the data is fetched, it needs to be prepared for analysis, which includes resampling, cleaning and structuring:

# Selecting only the relevant columns for backtesting
df = df[['price', 'shares', 'timestamp']]

# Indexing by timestamp to facilitate time series operations
df.set_index('timestamp', inplace=True)

ohlc_dict = {
'price': 'ohlc', # Aggregate to OHLC for the price
'shares': 'sum' # Sum up the shares for volume
}
resampled_df = df.resample('5S').agg(ohlc_dict).dropna() # '1T' for 1 minute; adjust as needed
resampled_df.columns = resampled_df.columns.droplevel(0) # flatten the column MultiIndex

# Check for NaN or infinite values in the DataFrame
print(resampled_df.isnull().sum())
print(np.isinf(resampled_df).sum())
Resampled Data
Checking data inconsistency

Create Strategy for Backtesting

After preparing the data, the next step is to define the trading strategy within a backtesting framework:

  • Strategy Class: Using Backtrader, a strategy class is created, incorporating the chosen technical indicators — Bollinger Bands, MACD, and RSI. This class defines the logic for when to enter and exit trades based on the conditions set by these indicators.
  • Setup: The strategy includes parameters for the indicators, which can be optimized later to refine the strategy’s performance.
class BollingerMACDRSIStrategy(bt.Strategy):
params = (
('maperiod', 20),
('rsi_period', 14),
('macd_fast', 12),
('macd_slow', 26),
('macd_signal', 9),
)

def __init__(self):
self.bollinger = bt.indicators.BollingerBands(self.datas[0], period=self.params.maperiod, devfactor=2)
self.macd = bt.indicators.MACD(self.datas[0], period_me1=self.params.macd_fast,
period_me2=self.params.macd_slow, period_signal=self.params.macd_signal)
self.rsi = bt.indicators.RSI_Safe(self.datas[0], period=self.params.rsi_period)
self.order = None
self.buyprice = None
self.buycomm = None
self.trade_count = 0 # Initialize trade counter

def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# An order has been submitted/accepted - no further action required
return

if order.status in [order.Completed]:
if order.isbuy():
self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
elif order.issell():
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))

elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')

def notify_trade(self, trade):
if trade.isclosed:
self.trade_count += 1 # Increment trade counter when a trade is closed

def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))

def next(self):
# Check if enough periods have passed for all indicators to be valid
if len(self) < max(self.params.maperiod, self.params.macd_slow):
return # Skip further processing until enough data is available

if not self.position:
if (self.data.close[0] > self.bollinger.lines.bot and
self.macd.lines.macd[0] > self.macd.lines.signal[0] and
self.rsi[0] < 30):
self.log('BUY CREATE, %.2f' % self.data.close[0])
self.order = self.buy()
else:
if (self.data.close[0] < self.bollinger.lines.top and
self.macd.lines.macd[0] < self.macd.lines.signal[0] and
self.rsi[0] > 70):
self.log('SELL CREATE, %.2f' % self.data.close[0])
self.order = self.sell()


def stop(self):
self.log('Ending Value %.2f' % self.broker.getvalue())
self.log('Total Trades Executed %d' % self.trade_count)

Running Backtesting

The final step is to run the backtest to evaluate the strategy’s effectiveness:

import backtrader as bt

# Create a Backtrader data feed from the DataFrame
data = bt.feeds.PandasData(dataname=resampled_df)

cerebro = bt.Cerebro()
cerebro.addstrategy(BollingerMACDRSIStrategy)

cerebro.adddata(data)

# Set our desired cash start
cerebro.broker.setcash(10000.0)

# Set the commission
cerebro.broker.setcommission(commission=0.001)

# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

# Run over everything
cerebro.run(maxcpus=2)

# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
Starting Portfolio Value: 10000.00
2024-03-25, BUY CREATE, 171.50
2024-03-25, BUY EXECUTED, Price: 171.50, Cost: 171.50, Comm 0.17
2024-03-25, SELL CREATE, 171.20
2024-03-25, SELL EXECUTED, Price: 171.18, Cost: 171.50, Comm 0.17
2024-03-25, BUY CREATE, 170.86
2024-03-25, BUY EXECUTED, Price: 170.86, Cost: 170.86, Comm 0.17
2024-03-26, SELL CREATE, 170.62
2024-03-26, SELL EXECUTED, Price: 170.61, Cost: 170.86, Comm 0.17
2024-03-26, BUY CREATE, 170.93
2024-03-26, BUY EXECUTED, Price: 170.94, Cost: 170.94, Comm 0.17
2024-03-26, SELL CREATE, 170.38
2024-03-26, SELL EXECUTED, Price: 170.38, Cost: 170.94, Comm 0.17
2024-03-27, BUY CREATE, 172.36
2024-03-27, BUY EXECUTED, Price: 172.36, Cost: 172.36, Comm 0.17
2024-03-27, SELL CREATE, 172.44
2024-03-27, SELL EXECUTED, Price: 172.45, Cost: 172.36, Comm 0.17
2024-03-27, BUY CREATE, 172.25
2024-03-27, BUY EXECUTED, Price: 172.25, Cost: 172.25, Comm 0.17
2024-03-27, SELL CREATE, 172.49
2024-03-27, SELL EXECUTED, Price: 172.49, Cost: 172.25, Comm 0.17
2024-03-28, BUY CREATE, 172.77
2024-03-28, BUY EXECUTED, Price: 172.77, Cost: 172.77, Comm 0.17
2024-03-28, SELL CREATE, 171.00
2024-03-28, SELL EXECUTED, Price: 170.99, Cost: 172.77, Comm 0.17
2024-03-28, BUY CREATE, 171.06
2024-03-28, BUY EXECUTED, Price: 171.06, Cost: 171.06, Comm 0.17
2024-03-28, SELL CREATE, 171.70
2024-03-28, SELL EXECUTED, Price: 171.70, Cost: 171.06, Comm 0.17
2024-03-28, BUY CREATE, 171.20
2024-03-28, BUY EXECUTED, Price: 171.21, Cost: 171.21, Comm 0.17
2024-04-01, SELL CREATE, 169.88
2024-04-01, SELL EXECUTED, Price: 169.88, Cost: 171.21, Comm 0.17
2024-04-02, BUY CREATE, 169.68
2024-04-02, BUY EXECUTED, Price: 169.64, Cost: 169.64, Comm 0.17
2024-04-02, SELL CREATE, 169.08
2024-04-02, SELL EXECUTED, Price: 169.07, Cost: 169.64, Comm 0.17
2024-04-02, BUY CREATE, 168.62
2024-04-02, BUY EXECUTED, Price: 168.62, Cost: 168.62, Comm 0.17
2024-04-03, SELL CREATE, 170.02
2024-04-03, SELL EXECUTED, Price: 170.04, Cost: 168.62, Comm 0.17
2024-04-03, BUY CREATE, 170.06
2024-04-03, BUY EXECUTED, Price: 170.05, Cost: 170.05, Comm 0.17
2024-04-04, SELL CREATE, 170.94
2024-04-04, SELL EXECUTED, Price: 170.94, Cost: 170.05, Comm 0.17
2024-04-04, BUY CREATE, 171.13
2024-04-04, BUY EXECUTED, Price: 171.13, Cost: 171.13, Comm 0.17
2024-04-04, Ending Value 9992.73
2024-04-04, Total Trades Executed 11
Final Portfolio Value: 9992.73
import pandas as pd
import numpy as np

# Calculate Simple Moving Average, Standard Deviation, Bollinger Bands
window = 20
resampled_df['sma'] = resampled_df['close'].rolling(window=window).mean()
resampled_df['rstd'] = resampled_df['close'].rolling(window=window).std()
resampled_df['bollinger_high'] = resampled_df['sma'] + 2 * resampled_df['rstd']
resampled_df['bollinger_low'] = resampled_df['sma'] - 2 * resampled_df['rstd']

# Calculate MACD
exp1 = resampled_df['close'].ewm(span=12, adjust=False).mean()
exp2 = resampled_df['close'].ewm(span=26, adjust=False).mean()
resampled_df['macd'] = exp1 - exp2
resampled_df['signal_line'] = resampled_df['macd'].ewm(span=9, adjust=False).mean()

# Calculate RSI
delta = resampled_df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
resampled_df['rsi'] = 100 - (100 / (1 + rs))

# Identify Buy and Sell Signals
resampled_df['buy_signal'] = ((resampled_df['close'] < resampled_df['bollinger_low']) &
(resampled_df['macd'] > resampled_df['signal_line']) &
(resampled_df['rsi'] < 30))

resampled_df['sell_signal'] = ((resampled_df['close'] > resampled_df['bollinger_high']) &
(resampled_df['macd'] < resampled_df['signal_line']) &
(resampled_df['rsi'] > 70))
import matplotlib.pyplot as plt
%matplotlib inline
# Create a new figure and set the size
plt.figure(figsize=(14, 9))

# Plot close price and Bollinger Bands
plt.plot(resampled_df.index, resampled_df['close'], label='Close Price', color='blue')
plt.plot(resampled_df.index, resampled_df['bollinger_high'], label='Bollinger High', color='red', linestyle='--')
plt.plot(resampled_df.index, resampled_df['bollinger_low'], label='Bollinger Low', color='green', linestyle='--')

# Plot buy and sell signals
plt.plot(resampled_df[resampled_df['buy_signal']].index,
resampled_df['close'][resampled_df['buy_signal']],
'^', markersize=12, color='green', lw=0, label='Buy Signal')
plt.plot(resampled_df[resampled_df['sell_signal']].index,
resampled_df['close'][resampled_df['sell_signal']],
'v', markersize=12, color='red', lw=0, label='Sell Signal')

plt.title('Trading Signals on Close Price with Bollinger Bands')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.show()

Chart Details:

  • Multi-panel Plot: This setup uses multiple subplots to clearly separate each aspect of the trading strategy for detailed analysis.
  • Trade Markers: Trades are visually marked on the price plot to show where buys and sells occurred relative to the price action.
  • Indicator Analysis: Each indicator has its own subplot for clarity, with additional lines or bars to highlight specific conditions like overbought or oversold levels in RSI or the difference between MACD and its signal line.

Backtesting Result Analysis

To quantify the backtesting results of your trading strategy effectively, you can use several key performance metrics that provide a clear and numerical indication of how the strategy performed over the test period. Here’s how to calculate and present these metrics:

1. Net Profit/Loss

This is the difference between the final portfolio value and the starting cash. It shows the absolute gain or loss made by the strategy.

2. Percentage Return

This is the net profit or loss expressed as a percentage of the starting portfolio value.

3. Drawdown

The maximum drawdown represents the largest single drop from peak to trough in the value of the portfolio, before a new peak is achieved. It’s an important measure of downside risk.

4. Sharpe Ratio

This measures the performance of the investment compared to a risk-free asset, after adjusting for its risk. It’s a useful way to understand the return of an investment compared to its risk.

5. Number of Trades

This is the total number of trades that were executed during the backtesting period.

Here is how we can calculate them using python:

import backtrader.analyzers as btanalyzers

cerebro = bt.Cerebro()
cerebro.addstrategy(BollingerMACDRSIStrategy)

cerebro.adddata(data)

# Adding Sharpe Ratio Analyzer with a risk-free rate, assuming a 0% risk-free rate for simplicity
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe', riskfreerate=0.0)
cerebro.addanalyzer(btanalyzers.DrawDown, _name='mydrawdown')
cerebro.addanalyzer(btanalyzers.Returns, _name='myreturns')

# Set our desired cash start
cerebro.broker.setcash(10000.0)

# Set the commission
cerebro.broker.setcommission(commission=0.001)

# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

starting_portfolio = cerebro.broker.getvalue()

# Run over everything
results = cerebro.run(maxcpus=2)

final_portfolio = cerebro.broker.getvalue()

strategy = results[0]
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

# Calculate net profit/loss
net_profit = final_portfolio - starting_portfolio
percentage_return = (net_profit / starting_portfolio) * 100


# Retrieve analysis from the built-in analyzers
sharpe_ratio = results[0].analyzers.mysharpe.get_analysis().get('sharperatio', None)

if sharpe_ratio is None:
sharpe_ratio_str = 'N/A'
else:
sharpe_ratio_str = f"{sharpe_ratio:.2f}"

drawdown_info = results[0].analyzers.mydrawdown.get_analysis()['max']['drawdown']
total_trades = results[0].trade_count

# Display the results
print(f"Starting Portfolio Value: ${starting_portfolio:,.2f}")
print(f"Final Portfolio Value: ${final_portfolio:,.2f}")
print(f"Net Profit/Loss: ${net_profit:,.2f}")
print(f"Percentage Return: {percentage_return:.2f}%")
print(f"Sharpe Ratio: {sharpe_ratio_str}")
print(f"Maximum Drawdown: {drawdown_info:.2f}%")
print(f"Total Trades Executed: {total_trades}")

Starting Portfolio Value: 10000.00
2024-03-25, BUY CREATE, 171.50
2024-03-25, BUY EXECUTED, Price: 171.50, Cost: 171.50, Comm 0.17
2024-03-25, SELL CREATE, 171.20
2024-03-25, SELL EXECUTED, Price: 171.18, Cost: 171.50, Comm 0.17
2024-03-25, BUY CREATE, 170.86
2024-03-25, BUY EXECUTED, Price: 170.86, Cost: 170.86, Comm 0.17
2024-03-26, SELL CREATE, 170.62
2024-03-26, SELL EXECUTED, Price: 170.61, Cost: 170.86, Comm 0.17
2024-03-26, BUY CREATE, 170.93
2024-03-26, BUY EXECUTED, Price: 170.94, Cost: 170.94, Comm 0.17
2024-03-26, SELL CREATE, 170.38
2024-03-26, SELL EXECUTED, Price: 170.38, Cost: 170.94, Comm 0.17
2024-03-27, BUY CREATE, 172.36
2024-03-27, BUY EXECUTED, Price: 172.36, Cost: 172.36, Comm 0.17
2024-03-27, SELL CREATE, 172.44
2024-03-27, SELL EXECUTED, Price: 172.45, Cost: 172.36, Comm 0.17
2024-03-27, BUY CREATE, 172.25
2024-03-27, BUY EXECUTED, Price: 172.25, Cost: 172.25, Comm 0.17
2024-03-27, SELL CREATE, 172.49
2024-03-27, SELL EXECUTED, Price: 172.49, Cost: 172.25, Comm 0.17
2024-03-28, BUY CREATE, 172.77
2024-03-28, BUY EXECUTED, Price: 172.77, Cost: 172.77, Comm 0.17
2024-03-28, SELL CREATE, 171.00
2024-03-28, SELL EXECUTED, Price: 170.99, Cost: 172.77, Comm 0.17
2024-03-28, BUY CREATE, 171.06
2024-03-28, BUY EXECUTED, Price: 171.06, Cost: 171.06, Comm 0.17
2024-03-28, SELL CREATE, 171.70
2024-03-28, SELL EXECUTED, Price: 171.70, Cost: 171.06, Comm 0.17
2024-03-28, BUY CREATE, 171.20
2024-03-28, BUY EXECUTED, Price: 171.21, Cost: 171.21, Comm 0.17
2024-04-01, SELL CREATE, 169.88
2024-04-01, SELL EXECUTED, Price: 169.88, Cost: 171.21, Comm 0.17
2024-04-02, BUY CREATE, 169.68
2024-04-02, BUY EXECUTED, Price: 169.64, Cost: 169.64, Comm 0.17
2024-04-02, SELL CREATE, 169.08
2024-04-02, SELL EXECUTED, Price: 169.07, Cost: 169.64, Comm 0.17
2024-04-02, BUY CREATE, 168.62
2024-04-02, BUY EXECUTED, Price: 168.62, Cost: 168.62, Comm 0.17
2024-04-03, SELL CREATE, 170.02
2024-04-03, SELL EXECUTED, Price: 170.04, Cost: 168.62, Comm 0.17
2024-04-03, BUY CREATE, 170.06
2024-04-03, BUY EXECUTED, Price: 170.05, Cost: 170.05, Comm 0.17
2024-04-04, SELL CREATE, 170.94
2024-04-04, SELL EXECUTED, Price: 170.94, Cost: 170.05, Comm 0.17
2024-04-04, BUY CREATE, 171.13
2024-04-04, BUY EXECUTED, Price: 171.13, Cost: 171.13, Comm 0.17
2024-04-04, Ending Value 9992.73
2024-04-04, Total Trades Executed 11
Final Portfolio Value: 9992.73
Starting Portfolio Value: $10,000.00
Final Portfolio Value: $9,992.73
Net Profit/Loss: $-7.27
Percentage Return: -0.07%
Sharpe Ratio: N/A
Maximum Drawdown: 0.35%
Total Trades Executed: 11

Interpretation of Results

  1. Profitability: The strategy resulted in a marginal loss of $7.27, which translates to a -0.07% return on the initial investment. This marginal loss suggests that the strategy, in its current form, barely deviates from breakeven, essentially maintaining the initial capital with slight erosion.
  2. Risk and Drawdown: The maximum drawdown reported is quite low (0.35%), which indicates that the strategy does not expose the portfolio to significant drops in value. This is a positive aspect as it suggests the strategy manages risks conservatively.
  3. Sharpe Ratio: The fact that the Sharpe Ratio is not available might indicate insufficient positive returns above the risk-free rate to calculate a meaningful ratio, or it might be due to the zero risk-free rate setup in the analysis tool.
  4. Trade Volume: With 11 trades executed during the period, the strategy is moderately active, suggesting it does not overtrade or sit idle for too long, which is a balanced approach in maintaining trade activity.

Recommendations for Improvement

  1. Indicator Optimization: Consider adjusting the parameters of the MACD, RSI, and Bollinger Bands. For example, tweaking the periods used for these indicators or the thresholds for buy/sell signals might align better with prevailing market conditions.
  2. Enhanced Risk Management: Introduce dynamic stop-loss and take-profit levels based on volatility measures like the Average True Range (ATR) to protect gains and limit losses more effectively.
  3. Diversification: Apply the strategy across a broader range of instruments. Diversifying by asset class or incorporating different markets (e.g., equities, forex, commodities) could reduce risk and potentially increase returns.
  4. Additional Indicators: Incorporate additional technical indicators or a fundamental analysis overlay to provide more robust trading signals. Combining multiple indicators can help confirm signals and reduce the likelihood of false positives.
  5. Machine Learning Models: Consider integrating machine learning techniques to predict price movements based on historical data, which could refine the strategy’s entry and exit points.
  6. Backtest Over Longer Periods: Extending the backtesting period or testing across different market phases (bullish, bearish, sideways) could provide deeper insights into the strategy’s effectiveness across various conditions.

Conclusion

Our exploration of backtesting a scalping strategy using Bollinger Bands,
MACD, and RSI has been enlightening. These technical indicators, key to
identifying precise buy and sell signals, have proven their effectiveness in real-time market conditions designed for high-frequency trading setups. The detail of tick data has been crucial, capturing minute-by-minute price movements essential for strategies that focus on small, quick gains.

Throughout our analysis, we’ve relied on the detailed data provided by a
reliable API. This support has been vital in both the development and
refinement of our trading strategies. The depth and range of data offered by platforms like EODHD highlight the importance of having a solid data
foundation. As we conclude, it’s clear that integrating detailed tick data and
advanced analytical tools can significantly enhance the performance of trading strategies, providing a strong base for those engaging with the complexities of algorithmic trading.

--

--