Die Strategie kauft, wenn der Chande Momentum Oszillator unter einen unteren Schwellenwert fällt, und schließt die Position, wenn er über einen oberen Schwellenwert steigt oder nach einer festen Anzahl von Bars.
Tests deuten auf eine durchschnittliche jährliche Rendite von etwa 40% hin. Am besten funktioniert sie in Trendmärkten.
Der Oszillator vergleicht jüngste Gewinne und Verluste zur Einschätzung des Momentums. Extreme negative Werte deuten auf überverkaufte Bedingungen hin, die die Strategie für Long-Einstiege nutzt. Positionen werden geschlossen, wenn das Momentum positiv wird oder der Haltezeitraum abläuft.
Details
Einstiegskriterien: CMO < LowerThreshold.
Long/Short: Nur Long.
Ausstiegskriterien: CMO > UpperThreshold oder MaxBarsInPosition Bars verstrichen.
Stops: Nein.
Standardwerte:
CmoPeriod = 9
LowerThreshold = -50
UpperThreshold = 50
MaxBarsInPosition = 5
CandleType = TimeSpan.FromMinutes(1)
Filter:
Kategorie: Mean Reversion
Richtung: Nur Long
Indikatoren: CMO
Stops: Nein
Komplexität: Grundlegend
Zeitrahmen: Intraday (1m)
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
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()