Testing indicates an average annual return of about 91%. It performs best in the stocks market.
ROC Impulse captures sudden bursts in the Rate of Change indicator. Sharp positive spikes lead to long trades and sharp negatives to short trades. When momentum fades back toward zero the position is closed.
The trigger levels can be tuned to react only to exceptional momentum events. ATR-based stops help prevent large losses if the spike quickly reverses.
Details
Entry Criteria: Signals based on ATR, ROC, Momentum.
Long/Short: Both directions.
Exit Criteria: Opposite signal or stop.
Stops: Yes.
Default Values:
RocPeriod = 12
AtrMultiplier = 2m
CandleType = TimeSpan.FromMinutes(5)
Filters:
Category: Trend
Direction: Both
Indicators: ATR, ROC, Momentum
Stops: Yes
Complexity: Basic
Timeframe: Intraday (5m)
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
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 Rate of Change / Momentum impulse.
/// Uses Momentum indicator crossing zero as signal for entries.
/// </summary>
public class RocImpulseStrategy : Strategy
{
private readonly StrategyParam<int> _rocPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom;
private bool _hasPrevValues;
private int _cooldown;
/// <summary>
/// Momentum period.
/// </summary>
public int RocPeriod
{
get => _rocPeriod.Value;
set => _rocPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="RocImpulseStrategy"/>.
/// </summary>
public RocImpulseStrategy()
{
_rocPeriod = Param(nameof(RocPeriod), 12)
.SetDisplay("Momentum Period", "Period for Momentum calculation", "Indicators")
.SetOptimize(8, 20, 4);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrevValues = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var momentum = new Momentum { Length = RocPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, momentum);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal momValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_hasPrevValues)
{
_hasPrevValues = true;
_prevMom = momValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevMom = momValue;
return;
}
// Momentum crosses above zero - buy signal
if (_prevMom <= 0 && momValue > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 55;
}
// Momentum crosses below zero - sell signal
else if (_prevMom >= 0 && momValue < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 55;
}
_prevMom = momValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Momentum
from StockSharp.Algo.Strategies import Strategy
class roc_impulse_strategy(Strategy):
"""
Strategy based on Rate of Change / Momentum impulse.
Uses Momentum indicator crossing zero as signal for entries.
"""
def __init__(self):
super(roc_impulse_strategy, self).__init__()
self._roc_period = self.Param("RocPeriod", 12) \
.SetDisplay("Momentum Period", "Period for Momentum calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_mom = 0.0
self._has_prev_values = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(roc_impulse_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev_values = False
self._cooldown = 0
def OnStarted2(self, time):
super(roc_impulse_strategy, self).OnStarted2(time)
momentum = Momentum()
momentum.Length = self._roc_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(momentum, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, momentum)
self.DrawOwnTrades(area)
def _process_candle(self, candle, mom_value):
if candle.State != CandleStates.Finished:
return
mom = float(mom_value)
if not self._has_prev_values:
self._has_prev_values = True
self._prev_mom = mom
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_mom = mom
return
if self._prev_mom <= 0 and mom > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown = 55
elif self._prev_mom >= 0 and mom < 0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown = 55
self._prev_mom = mom
def CreateClone(self):
return roc_impulse_strategy()