Products
Platform
Research
Market
Learn
Partner
Support
IPO
Logo_light
Module 5
Popular Algo Trading Strategies
Course Index

Chapter 1 | 3 min read

Algo Strategy: Momentum Strategy - Ride the wave

Ever noticed how some stocks just keep going up after breaking a level? Or suddenly crash after crossing a support?

That’s momentum – when prices move in a certain direction with force. Algo traders love to ride this wave.

In simple words:

“Buy high, sell higher.” “Sell low, cover lower.”

Momentum strategies don’t wait for dips — they jump in when prices show strong strength (up or down), assuming the trend will continue.

Here are common signals used:

  • Moving Average Crossover

When a short-term MA (say 10-day) crosses above a long-term MA (say 50-day), it indicates upward momentum.

Your algo can buy here and exit when the crossover reverses.

import pandas as pd
import talib

close_prices = df['Close']   
# <-- Add this line before SMA
short_ma = talib.SMA(close
_prices, 
timeperiod=10)
long_ma = talib.SMA(close
_prices,
timeperiod=50)

#Buy signal when short MA
 > long MA
if short_ma[-1] > long_ma[-1]
and short_ma[-2] <= long_ma
[-2]:
print("Buy Signal: 
Moving Average Crossover")

Note:

This code uses the TA-Lib library to detect a moving average crossover — a common trading signal.

  • short_ma & long_ma: Calculated using TA-Lib’s SMA() function. Here, the short-term moving average uses a 10-period window, and the long-term moving average uses a 50-period window. Both are based on close_prices, which is assumed to be a Pandas Series of closing prices.

  • Signal logic:

o short_ma[-1] > long_ma[-1] → In the most recent candle, the short-term MA is above the long-term MA (bullish).
o short_ma[-2] <= long_ma[-2] → In the previous candle, the short-term MA was at or below the long-term MA.
o Together, these conditions detect the exact moment when the short MA crosses above the long MA — a “golden cross” indicating a potential buy.
o print(): Outputs the signal text to confirm the crossover event.

This structure ensures you only trigger a buy signal on the crossover itself, not on every bar where the short MA remains above the long MA.

  • Breakout from Resistance

Price breaks a previous high after consolidation = breakout. Your algo enters long as soon as breakout candle closes above resistance.

resistance = max(close_prices
[-20
:-1])  
#Highest price in last 20 
candles
if close_prices[-1] > 
resistance: 
print("Buy Signal: 
Breakout from Resistance")

Note: Finds the highest close in the last 20 candles. If today’s close is higher, it’s a breakout buy signal.

  • RSI Crossing 60 or 70
    If RSI crosses 60, it often shows strength. Some algos use RSI > 70 for momentum long entry.
import talib

rsi = talib.RSI(df['Close'], 
timeperiod=14) </br>
if rsi[-1] > 60 and rsi[-2] 
<= 60: 
</br>
print("Buy Signal: RSI
 Crossing
 60") 
    </br>
elif rsi[-1] > 70 and rsi[-2] 
<= 70: 
</br>
    print("Strong Buy Signal: 
    RSI Crossing 70") 
    </br>

Note:

  • talib.RSI() → Calculates Relative Strength Index (momentum indicator).
  • timeperiod=14 → Uses 14 candles for calculation.
  • rsi[-1] → Latest RSI value.
  • rsi[-2] → RSI value from the previous candle.
  • First condition → Detects when RSI crosses above 60 (possible upward trend).
  • Second condition → Detects when RSI crosses above 70 (strong bullish momentum).

Let’s say you define:

  • Buy if price closes above 20-day high
  • Sell if price falls below 10-day low

You run this strategy on NIFTY stocks every day. Your algo scans all 50 stocks at 9:30 AM, checks signals, and enters trades.

This way, your code is always hunting for momentum breakouts, automatically.

import yfinance as yf

# Define universe
stocks = ["RELIANCE.NS", 
"TCS.NS",
"INFY.NS", "HDFCBANK.NS", 
"ICICIBANK.NS"]

# Store signals as (symbol,
 side)
signals = []

for s in stocks:
# Download last 1 month daily
 data
df = yf.download(s, period=
"1mo", interval="1d")

# Skip if data is missing
 or too
 short
if df is None or 
df.empty or len(df) < 21:
continue

last_close = df['Close']
.iloc[-1]

# Highest High of last 20 days
(excluding current bar)
prev_20_high = 
df['High'].
iloc[-21:-1].max()

# Lowest Low of last 10 days 
(excluding current bar)
prev_10_low = df['Low'].
iloc[-11:-1].min()

 # Momentum BUY condition
  if last_close > prev_20_high:
 signals.append((s, "BUY"))

 # Momentum SELL condition
 elif last_close < prev_10_low:
 signals.append((s, "SELL"))

# Print signals
print("Momentum Signals:")
for sym, side in signals:
print(f"{sym}: {side}")

# -Order placement 
(placeholder) -
# Replace with your broker 
API call
def place_market_order
(api_client, symbol, qty, 
side):
    """
    Example placeholder for 
    broker order placement.
    Modify according 
    to your broker API.
    """
print(f"Placing {side} order 
 for {qty} shares of {symbol}")

# Example loop to place trades
api_client = None  
# <-- initialize with your 
broker client
for sym, side in signals:
symbol_for_broker = 
sym.replace(".NS", "")  
# adjust if your broker 
needs NSE suffix dropped
place_market_order(api_client, 
symbol_for_broker, qty=1, 
side=side)

Note:

  1. stocks → List of stock tickers (symbols) the algo will scan. Example: Reliance, TCS.

  2. df = yf.download(...) → Downloads price data from Yahoo Finance.
    a. period="1mo" → Gets 1 month of past data.
    b. interval="1d" → Each data point is 1 day (daily candles).

  3. df['Close'].iloc[-1] → last_close → The most recent closing price (yesterday’s close if you run in morning).

  4. df['High'].iloc[-21:-1].max() → prev_20_high
    a. Looks back at the last 20 days (excluding the latest day).
    b. Finds the highest price in that period.
    c. Used to detect breakout above recent highs.

  5. df['Low'].iloc[-11:-1].min() → prev_10_low
    a. Looks back at the last 10 days (excluding the latest day).
    b. Finds the lowest price in that period.
    c. Used to detect breakdown below recent lows.

  6. signals.append((s, "BUY")) → Stores a buy signal if last_close > prev_20_high.

  7. signals.append((s, "SELL")) → Stores a sell signal if last_close < prev_10_low.

  8. print("Momentum Signals:") → Prints which stocks got buy/sell signals.

  9. place_market_order(api_client, symbol, qty, side)
    Placeholder function that would send the order to your broker API.
    a. api_client → The broker connection (to be initialized with your broker).
    b. symbol → Stock ticker, formatted for your broker.
    c. qty → Number of shares to buy/sell (currently fixed at 1).
    d. side → BUY or SELL, depending on the signal.

# Assume you have already 
authenticated with your broker's
 API
# and have an 'api_client' 
object ready for placing orders.

def place_market_order
(api_client, symbol, qty, side
="BUY", 
exchange="NSE", product="CNC"):
    """
Places a market order 
for the given symbol.

Parameters:
api_client : object - 
Your broker API client instance
symbol     : str    
- Trading symbol 
(e.g., 'RELIANCE' or
 'RELIANCE.NS')
qty        : int    
- Quantity to buy/sell
side       : str    
- "BUY" or "SELL"
exchange   : str    
- Market exchange (default NSE)
product    : str   
- Product type 
("CNC" for delivery, 
"MIS" for intraday)
    """
 try:
 response = api_client.place
_order(
tradingsymbol=symbol,
exchange=exchange,
transaction_type=side,
quantity=qty,
order_type="MARKET",
product=product  # 
<- changed from 
product_type to product
)
print(f"Order placed for
{symbol}: {response}")
except Exception as e:
print(f"Error placing order for
{symbol}: {repr(e)}")

# Example usage: 
Place buy orders for all 
breakout stocks
for stock in breakout_stocks:
# keep .NS if broker expects it,
strip only if necessary
broker_symbol = 
stock.replace(".NS", "")  
place_market_order(api_client, 
broker_symbol, qty=1, side="BUY")

Note:
This function is just a shortcut to send market orders using your broker’s Python client.

  • You pass the details:
    o symbol → stock name (e.g., "RELIANCE")
    o qty → how many shares
    o side → "BUY" or "SELL"
    o exchange → default "NSE"
    o product → "CNC" for delivery, change to "MIS" for intraday

  • api_client.place_order(...) is only a placeholder — replace the field names to match your broker’s SDK.

  • try/except makes sure the code doesn’t crash if an order fails; it prints the error instead.

  • stock.replace(".NS","") removes the Yahoo suffix so "RELIANCE.NS" becomes "RELIANCE" (what brokers usually accept).

  • Orders here are market orders (instant at market price). If you want price control, use limit orders.

  • Safety tips before going live:

o Make sure qty > 0
o Run only during market hours
o Test on paper/sandbox mode first

  • Momentum works well in trending markets
  • Works best during strong market sentiment – like earnings season or budget week
  • Algos can catch these moves faster than humans
  • Momentum strategies fail in sideways markets
  • Need tight stoploss – sudden reversals can wipe profits
  • Not suitable for gap-up/gap-down days unless adjusted
  • Intraday momentum (e.g., price breaks previous day’s high)
  • Sector rotation momentum (e.g., shift from IT to Auto)
  • News-based momentum (e.g., RBI rate cut = Bank stocks surge)

In short: Momentum algos try to ride the wave, not fight it. In the next chapter, we’ll look at the opposite of momentum — Mean Reversion Strategies.

Is this chapter helpful?
Previous
Deploying Your Algo for Live Trading
Next
Algo Strategy: Mean Reversion – Buy the Dip, Sell the Spike

Discover our extensive knowledge center

Explore our comprehensive video library that blends expert market insights with Kotak's innovative financial solutions to support your goals.