52-Week High
This momentum strategy focuses on stocks making new 52‑week highs, which often continue to outperform. The system buys breakouts and holds until momentum fades.
Qualifying stocks are filtered for liquidity and fundamental strength.
Details
- Data: Daily highs over the past year and volume.
- Entry: Buy when price crosses above prior 52‑week high.
- Exit: Sell on 10% pullback or new low.
- Instruments: Equities.
- Risk: Position sizing by volatility.
// Weeks52HighStrategy.cs
// -----------------------------------------------------------------------------
// 52-week high proximity strategy.
// Buys when price is near its highest level (close to 52-week high),
// sells when price drops significantly below the high.
// Uses Highest indicator to track the rolling high.
// Cooldown prevents excessive trading.
// -----------------------------------------------------------------------------
// Date: 2 Aug 2025
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the 52-week high proximity effect.
/// </summary>
public class Weeks52HighStrategy : Strategy
{
private readonly StrategyParam<int> _highPeriod;
private readonly StrategyParam<decimal> _entryRatio;
private readonly StrategyParam<decimal> _exitRatio;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Lookback period for highest high.
/// </summary>
public int HighPeriod
{
get => _highPeriod.Value;
set => _highPeriod.Value = value;
}
/// <summary>
/// Minimum ratio of current price to high to enter (e.g. 0.95 = within 5% of high).
/// </summary>
public decimal EntryRatio
{
get => _entryRatio.Value;
set => _entryRatio.Value = value;
}
/// <summary>
/// Exit when price drops below this ratio of the high.
/// </summary>
public decimal ExitRatio
{
get => _exitRatio.Value;
set => _exitRatio.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// The type of candles to use for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
private Highest _highest;
private int _cooldownRemaining;
public Weeks52HighStrategy()
{
_highPeriod = Param(nameof(HighPeriod), 50)
.SetDisplay("High Period", "Rolling high lookback period", "Parameters");
_entryRatio = Param(nameof(EntryRatio), 0.97m)
.SetDisplay("Entry Ratio", "Min price/high ratio to enter", "Parameters");
_exitRatio = Param(nameof(ExitRatio), 0.92m)
.SetDisplay("Exit Ratio", "Exit when price/high drops below this", "Parameters");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = HighPeriod };
SubscribeCandles(CandleType)
.Bind(_highest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_highest.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
if (highestValue <= 0)
return;
var ratio = candle.ClosePrice / highestValue;
// Price is near the high -> momentum effect -> buy
if (ratio >= EntryRatio && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Price has dropped far from the high -> exit
else if (ratio <= ExitRatio && Position > 0)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest
from StockSharp.Algo.Strategies import Strategy
class weeks52_high_strategy(Strategy):
"""Strategy based on the 52-week high proximity effect."""
def __init__(self):
super(weeks52_high_strategy, self).__init__()
self._high_period = self.Param("HighPeriod", 50) \
.SetDisplay("High Period", "Rolling high lookback period", "Parameters")
self._entry_ratio = self.Param("EntryRatio", 0.97) \
.SetDisplay("Entry Ratio", "Min price/high ratio to enter", "Parameters")
self._exit_ratio = self.Param("ExitRatio", 0.92) \
.SetDisplay("Exit Ratio", "Exit when price/high drops below this", "Parameters")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(weeks52_high_strategy, self).OnReseted()
self._highest = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(weeks52_high_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = int(self._high_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._highest, self._process_candle).Start()
def _process_candle(self, candle, highest_val):
if candle.State != CandleStates.Finished:
return
if not self._highest.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
hv = float(highest_val)
if hv <= 0:
return
ratio = float(candle.ClosePrice) / hv
entry_ratio = float(self._entry_ratio.Value)
exit_ratio = float(self._exit_ratio.Value)
cooldown = int(self._cooldown_bars.Value)
if ratio >= entry_ratio and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif ratio <= exit_ratio and self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return weeks52_high_strategy()