Estrategia basada en el impulso del Rate of Change (ROC)
Las pruebas indican un retorno anual promedio de aproximadamente 91%. Funciona mejor en el mercado de acciones.
ROC Impulse captura explosiones repentinas en el indicador Rate of Change. Los picos positivos marcados llevan a operaciones largas y los picos negativos marcados a operaciones cortas. Cuando el momentum se desvanece hacia cero se cierra la posición.
Los niveles de activación se pueden ajustar para reaccionar solo ante eventos de momentum excepcionales. Los stops basados en ATR ayudan a prevenir grandes pérdidas si el pico revierte rápidamente.
Detalles
Criterios de entrada: Señales basadas en ATR, ROC, Momentum.
Largo/Corto: Ambas direcciones.
Criterios de salida: Señal opuesta o stop.
Stops: Sí.
Valores predeterminados:
RocPeriod = 12
AtrMultiplier = 2m
CandleType = TimeSpan.FromMinutes(5)
Filtros:
Categoría: Tendencia
Dirección: Ambos
Indicadores: ATR, ROC, Momentum
Stops: Sí
Complejidad: Básico
Marco temporal: Intradía (5m)
Estacionalidad: No
Neural Networks: No
Divergencia: No
Nivel de riesgo: Medio
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()