Whereas momentum and trend-following algorithms share sure similarities, they’ve notable variations. For instance, the Transferring Common Convergence Divergence (MACD) indicator capabilities as each a momentum and a trend-following software.
Momentum algorithms, a subset of trend-following methods, typically give attention to short-term worth actions. They intention to determine if a inventory has just lately demonstrated sturdy momentum, assuming the development will persist for a short time.
In distinction, trend-following algorithms are oriented towards figuring out long-term tendencies and capitalize on the directional motion of those tendencies, whatever the time it takes for income to materialize.
Momentum buying and selling encompasses a number of methods, together with however not restricted to the next:
- Value Fee of Change (ROC)
- Absolute Momentum
- Relative Momentum
- Twin Momentum
Every of those algorithms shall be explored under, together with Python code to implement them. For every, I’ll first outline the methodology, describe the method used, and supply an instance implementation in Python from scratch.
Definition
The Value Fee of Change (ROC) is a momentum-based buying and selling technique that measures the proportion change in a inventory’s worth over a specified interval. A constructive ROC suggests the inventory is gaining momentum, triggering a purchase sign because the indicator anticipates continued worth development.
Formulation
Python Code Implementation
As an example this idea, I’ll use a dataframe containing Apple’s historic inventory costs. Particulars on how the dataset was obtained utilizing Python shall be shared on the conclusion of this information.
- Interval (n): Use a 10-day interval.
- ROC Calculation: Compute the proportion change in worth over 10 days.
- Alerts:
- If the ROC is constructive for a day, set off a purchase sign.
- If the ROC is destructive for a day, set off a promote sign.
4. Efficiency Evaluation:
- Calculate day by day returns primarily based on the technique.
- Compute cumulative returns to judge its effectiveness over time.
# Outline the time interval for calculating the ROC
n = 10# Calculate the ROC indicator
df['ROC'] = df['Adj Close'].pct_change(intervals=n)
# Generate purchase indicators when the ROC turns into above its sign line (0)
df['Buy'] = (df['ROC'] > 0) & (df['ROC'].shift(1) < 0)
# Generate promote indicators when the ROC turns into under its sign line (0)
df['Sell'] = (df['ROC'] < 0) & (df['ROC'].shift(1) > 0)
# Purchase securities when a purchase sign is generated and promote them when a promote sign is generated
# 1 for Purchase, -1 for Promote, 0 for Maintain
df['Signal'] = np.the place(df['Buy']==True, 1, np.the place(df['Sell']==True,-1,0))
# Calculate the day by day returns of the technique
df['Returns'] = df['Signal'].shift(1) * df['Adj Close'].pct_change()
df['Returns+1'] = 1 + df['Returns']
# Calculate the cumulative returns of the technique
df['Cumulative_Returns'] = (1+df['Returns']).cumprod()
# Print the ultimate cumulative return
print('Remaining Cumulative Return Over The Entire Interval: ', df['Cumulative_Returns'][-1]-1) Remaining Cumulative Return Over The Entire Interval: 0.04052975893266497
Plotting the cumulative return and the sign:
df[['Returns+1','Cumulative_Returns']].plot(figsize=(8,4))
plt.title("Value Fee of Change (ROC)")
plt.present()df[['Signal']].plot(figsize=(8,4))
plt.present()
The cumulative return through the timeframe from mid-August 2022 to mid-December 2022 didn’t present sturdy efficiency. To handle this, you may experiment with altering the look-back interval in days to find out essentially the most optimum interval. Take a look at intervals of 15 days and 5 days — evaluation reveals that the 5-day interval sometimes generates higher returns in comparison with the 15-day interval.
Absolute Momentum, additionally known as time-series momentum, entails buying property that exhibit constructive returns over a given timeframe and promoting these with destructive returns. This technique relies on analyzing historic efficiency inside a set interval to make buying and selling choices.
For implementing Absolute Momentum, the method entails the next steps:
- Calculate Inventory Returns: Decide the proportion change within the inventory worth for the specified interval.
- Generate a Buying and selling Sign: Set up indicators primarily based on the calculated returns — constructive returns indicate a purchase sign, whereas destructive returns set off a promote sign.
- Clean Alerts Utilizing Transferring Averages: To filter out noise, compute a shifting common of the uncooked indicators.
- Decide Remaining Alerts: Convert the smoothed sign into actionable buying and selling directions:
- Use +1+1+1 to point a purchase order, −1–1−1 for a promote order, and 000 to suggest holding the place.
def absolute_momentum(df, window):
"""
Calculate the day by day return
Calculate a sign: if return is constructive, then 1, if destructive -1, else 0
Calculate a shifting common over "window" interval to easy the sign
Calulate the ultimate sign:
if the smoothed sign is constructive then a purchase (1) order sign is triggered,
when destructive a promote order is trigerred (-1),
else keep in a maintain place (0)
"""
df['returns'] = df['Adj Close'].pct_change()
df['signals']=np.the place(df['returns']>0,1,np.the place(df['returns']<0,-1,0))
df['signals_ma']=df['signals'].rolling(window).imply()
df['signals_final']=np.the place(df['signals_ma']>0,1,np.the place(df['signals_ma']<0,-1,0))
return df#Calculate the indicators
df = absolute_momentum(df, 30)
df[['returns']].plot(figsize=(8,4))
plt.title("Returns")
plt.present()
df[['signals_ma','signals_final']].plot(figsize=(8,4))
plt.legend(loc = 'higher left')
plt.title("Absolute Momentum")
plt.present()
Within the second graph, the blue line represents the smoothed sign. When this line is above zero, it triggers a purchase sign represented by +1+1; when it falls under zero, a promote sign is generated and represented by −1–1.
Relative momentum is a method inside algorithmic buying and selling that evaluates how a inventory’s efficiency compares to that of the broader market or a selected benchmark. This method identifies shares which are outperforming or underperforming both the market as a complete or a selected index.
Moreover, relative momentum can assess the energy of a inventory’s efficiency over time relative to its personal historic conduct. That is helpful for figuring out whether or not a inventory has been overbought or oversold inside a given time window, typically utilizing the Relative Power Index (RSI) as a metric.
On this instance, the inventory beneath evaluation is Apple, and it’s benchmarked in opposition to the S&P 500 index. The steps are outlined as follows:
- Calculate 14-Day Transferring Common Returns: Compute the rolling imply returns for each the inventory and the index over a 14-day window.
- Compute the Relative Power Ratio: Divide the rolling common return of the inventory by that of the index to acquire the relative energy ratio.
- Generate the Relative Power Line: Apply a rolling imply to the relative energy ratio to easy it additional and observe tendencies.
- Set up Buying and selling Alerts: Create indicators to purchase or promote primarily based on whether or not the relative energy ratio is above or under the relative energy line.
window = 14benchmark_ticker = 'SPY'
benchmark_data = download_stock_data(benchmark_ticker,timestamp_start,timestamp_end).set_index('Date')
stock_returns = df['Adj Close'].pct_change()
benchmark_returns = benchmark_data['Adj Close'].pct_change()
# Calculate rolling imply of the return over 14 days for the inventory and the index
stock_rolling_mean = stock_returns.rolling(window=window).imply()
benchmark_rolling_mean = benchmark_returns.rolling(window=window).imply()
# Calculate relative energy
relative_strength = stock_rolling_mean / benchmark_rolling_mean
# Calculate rolling imply of relative energy
relative_strength_rolling_mean = relative_strength.rolling(window=window).imply()
# Create sign primarily based on relative energy rolling imply
sign = np.the place(relative_strength > relative_strength_rolling_mean, 1, -1)
df_rel=pd.concat((stock_rolling_mean,benchmark_rolling_mean,relative_strength,relative_strength_rolling_mean),axis=1)
df_rel.columns=['Stock_ma','Benchmark_ma','RS','RS_ma']
df_rel['signal']=sign
df_rel=df_temp.dropna()
df_rel.head()
download_stock_data* shall be defined in “Loading Dataset” half.
Right here is when plotting the entire interval:
df_rel[['RS','RS_ma']].plot(figsize=(6,4))
plt.title('Relative Momentum')df_rel[['signal']].plot(figsize=(6,4))
plt.title('Sign')
plt.present()
Let’s plot the ratio and the relative energy line for a sure interval (to see extra clearly):
df_rel.question("Date>'2022-08-30' and Date<'2022-11-30'") [['RS','RS_ma','signal']].plot(figsize=(8,8))
Definition
Twin Momentum is an algorithm that mixes two indicators: The relative energy and absolutely the momentum. It’s primarily based on the concept that when a inventory is performing nicely relative to its friends and on the similar time, its absolute momentum is constructive, it’s prone to proceed to carry out nicely within the close to future.
Python Code
On this instance, I construct 2 datasets:
- df_global: wherein we have now 4 shares (‘NFLX’, ‘AMZN’, ‘GOOG’, ‘AAPL’) that make up our portfolio. The purpose is to know which one has a powerful twin momentum.
- df_global_market: which represents the market or a selected benchmark. For illustration, I put solely 6 shares : ‘MSFT’, ‘META’, ‘GM’, ‘GS’, ‘TTE’, ‘F’.
Then:
- The momentum for every inventory within the portfolio is computed
- The momentum for the entire market is computed
- Then, the twin momentum is computed for every inventory within the portfolio
- Additionally a rank among the many 4 shares is given for the entire interval
def dual_momentum(df_global,df_global_market,window):
"""
dual_momentum:
Calculate the momentum for every inventory
Calculate the momentum of the market
Calculate the twin momentum because the product of the inventory's momentum and the market one
"""
# Calculate 10-days momentum of the costs
mother = df_global.pct_change(window).dropna()
# Calculate 10-days momentum of the worldwide inventory market
global_mom = df_global_market.pct_change(window).dropna()
global_mom_mean = global_mom.imply(axis=1)# Create an information body to carry the ultimate outcomes
outcomes = pd.DataFrame()
for ticker in df_global.columns:
mom_ticker = mother[ticker]
# Calculate the twin momentum rating for this inventory
dual_mom = pd.DataFrame(mom_ticker * global_mom_mean)
dual_mom.columns=[ticker+'_dual_mom']
outcomes=pd.concat((dual_mom,outcomes),axis=1)
return outcomes
window=10
outcomes=dual_momentum(df_global,df_global_market,window)
outcomes.iloc[-30:,:].plot(figsize=(12,6))
plt.title("Twin Momentum")
plt.present()
outcomes.apply(lambda x: x.rank(ascending=False),axis=1).iloc[-30:,:].plot(figsize=(12,6))
plt.title("Rank of Twin Momentum (1:Finest, 4:Worst)")
plt.present()
I solely plotted the final 30 days for readability within the chart:
Within the final interval, Google is ranked N°1 in opposition to the opposite shares, as a result of it’s displaying a powerful twin momentum. The next one within the rating is Apple. The final one is Netflix.
This rating, as you may see within the graph, was not fixed over the previous 30 days. Apple was typically ranked 4. Then again, we will see Netflix taking the primary place on the rostrum extra typically than the opposite shares.
There are completely different options to obtain datasets. Listed here are two strategies you should utilize:
First methodology
import pandas_datareader.knowledge as internet
import yfinance as yfinbegin = dt.datetime(2022, 1, 1)
finish = dt.datetime.right now()
yfin.pdr_override()
# Outline the ticker image for the inventory
ticker = 'AAPL'
# Load the inventory costs from Yahoo Finance
df = internet.DataReader(ticker, begin, finish)
df.tail()
I discover it slower than the second methodology, which I used for the entire datasets.
Second methodology
AAPL
def download_stock_data(ticker,timestamp_start,timestamp_end):
url=f"https://query1.finance.yahoo.com/v7/finance/obtain/{ticker}?period1={timestamp_start}&period2={timestamp_end}&interval
=1d&occasions=historical past&includeAdjustedClose=true"
df = pd.read_csv(url)
return dfdatetime_start=dt.datetime(2022, 1, 1, 7, 35, 51)
datetime_end=dt.datetime.right now()
# Convert to timestamp:
timestamp_start=int(datetime_start.timestamp())
timestamp_end=int(datetime_end.timestamp())
df = download_stock_data("AAPL",timestamp_start,timestamp_end)
df = df.set_index('Date')
SPY
benchmark_ticker = 'SPY'
benchmark_data = download_stock_data(benchmark_ticker,timestamp_start,timestamp_end).set_index('Date')
df_global with 4 shares:
tickers=['AAPL','GOOG','AMZN','NFLX']df_global=pd.DataFrame()
for ticker in tickers:
df = download_stock_data(ticker,timestamp_start,timestamp_end)[['Date','Adj Close']]
df = df.set_index('Date')
df.columns=[ticker]
df_global=pd.concat((df_global, df),axis=1)
df_global.head()
df_global_market with 6 shares
tickers=['MSFT','META','GM','GS','TTE','F']df_global_market=pd.DataFrame()
for ticker in tickers:
df = download_stock_data(ticker,timestamp_start,timestamp_end)[['Date','Adj Close']]
df = df.set_index('Date')
df.columns=[ticker]
df_global_market=pd.concat((df_global_market, df),axis=1)
df_global_market.head()