La estrategia compra cuando el Oscilador de Momentum Chande cae por debajo de un umbral inferior y cierra la posición cuando sube por encima de un umbral superior o tras un número fijo de barras.
Las pruebas indican un rendimiento anual promedio de aproximadamente 40%. Tiene el mejor desempeño en mercados con tendencia.
El oscilador compara las ganancias y pérdidas recientes para medir el momentum. Los valores negativos extremos sugieren condiciones de sobreventa, que la estrategia usa para entradas largas. Las posiciones se cierran cuando el momentum se vuelve positivo o expira el período de mantenimiento.
Detalles
Criterios de entrada: CMO < LowerThreshold.
Largo/Corto: Solo largos.
Criterios de salida: CMO > UpperThreshold o MaxBarsInPosition barras transcurridas.
Stops: No.
Valores predeterminados:
CmoPeriod = 9
LowerThreshold = -50
UpperThreshold = 50
MaxBarsInPosition = 5
CandleType = TimeSpan.FromMinutes(1)
Filtros:
Categoría: Reversión a la media
Dirección: Solo largos
Indicadores: CMO
Stops: No
Complejidad: Básico
Marco temporal: Intradía (1m)
Estacionalidad: No
Redes neuronales: 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>
/// Chande Momentum Oscillator strategy.
/// Long only: buys when CMO(CmoPeriod) is below LowerThreshold and closes the long when CMO rises above UpperThreshold or after
/// MaxBarsInPosition candles.
/// </summary>
public class ChandeMomentumOscillatorStrategy : Strategy
{
private readonly StrategyParam<int> _cmoPeriod;
private readonly StrategyParam<decimal> _lowerThreshold;
private readonly StrategyParam<decimal> _upperThreshold;
private readonly StrategyParam<int> _maxBarsInPosition;
private readonly StrategyParam<DataType> _candleType;
private int _barsInPosition;
/// <summary>
/// CMO period.
/// </summary>
public int CmoPeriod
{
get => _cmoPeriod.Value;
set => _cmoPeriod.Value = value;
}
/// <summary>
/// CMO level below which the strategy buys.
/// </summary>
public decimal LowerThreshold
{
get => _lowerThreshold.Value;
set => _lowerThreshold.Value = value;
}
/// <summary>
/// CMO level above which the long closes.
/// </summary>
public decimal UpperThreshold
{
get => _upperThreshold.Value;
set => _upperThreshold.Value = value;
}
/// <summary>
/// Candles a position is held at most.
/// </summary>
public int MaxBarsInPosition
{
get => _maxBarsInPosition.Value;
set => _maxBarsInPosition.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ChandeMomentumOscillatorStrategy()
{
_cmoPeriod = Param(nameof(CmoPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("CMO Period", "CMO period", "CMO");
_lowerThreshold = Param(nameof(LowerThreshold), -50m)
.SetDisplay("Lower Threshold", "CMO level below which the strategy buys", "CMO");
_upperThreshold = Param(nameof(UpperThreshold), 50m)
.SetDisplay("Upper Threshold", "CMO level above which the long closes", "CMO");
_maxBarsInPosition = Param(nameof(MaxBarsInPosition), 5)
.SetGreaterThanZero()
.SetDisplay("Max Bars In Position", "Candles a position is held at most", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
_barsInPosition = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barsInPosition = 0;
var cmo = new ChandeMomentumOscillator { Length = CmoPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(cmo, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, cmo);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue cmoValue)
{
if (candle.State != CandleStates.Finished)
return;
if (Position > 0)
_barsInPosition++;
if (!cmoValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var cmo = cmoValue.GetValue<decimal>();
if (Position > 0)
{
if (cmo > UpperThreshold || _barsInPosition >= MaxBarsInPosition)
SellMarket(Position);
return;
}
if (Position == 0 && cmo < LowerThreshold)
{
BuyMarket(Volume);
_barsInPosition = 0;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ChandeMomentumOscillator
from StockSharp.Algo.Strategies import Strategy
class chande_momentum_oscillator_strategy(Strategy):
"""
Chande Momentum Oscillator strategy.
Long only: buys when CMO(CmoPeriod) is below LowerThreshold and closes the long when CMO rises above UpperThreshold or after
MaxBarsInPosition candles.
"""
def __init__(self):
super(chande_momentum_oscillator_strategy, self).__init__()
self._cmo_period = self.Param("CmoPeriod", 9).SetGreaterThanZero().SetDisplay("CMO Period", "CMO period", "CMO")
self._lower_threshold = self.Param("LowerThreshold", -50.0).SetDisplay("Lower Threshold", "CMO level below which the strategy buys", "CMO")
self._upper_threshold = self.Param("UpperThreshold", 50.0).SetDisplay("Upper Threshold", "CMO level above which the long closes", "CMO")
self._max_bars_in_position = self.Param("MaxBarsInPosition", 5).SetGreaterThanZero().SetDisplay("Max Bars In Position", "Candles a position is held at most", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._bars_in_position = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(chande_momentum_oscillator_strategy, self).OnReseted()
self._bars_in_position = 0
def OnStarted2(self, time):
super(chande_momentum_oscillator_strategy, self).OnStarted2(time)
self._bars_in_position = 0
cmo = ChandeMomentumOscillator()
cmo.Length = self._cmo_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(cmo, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, cmo)
def _process_candle(self, candle, cmo_value):
if candle.State != CandleStates.Finished:
return
if self.Position > 0:
self._bars_in_position += 1
if not cmo_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cmo = cmo_value.GetValue[Decimal](None)
if self.Position > 0:
if cmo > Decimal(self._upper_threshold.Value) or self._bars_in_position >= self._max_bars_in_position.Value:
self.SellMarket(self.Position)
return
if self.Position == 0 and cmo < Decimal(self._lower_threshold.Value):
self.BuyMarket(self.Volume)
self._bars_in_position = 0
def CreateClone(self):
return chande_momentum_oscillator_strategy()