Neon Momentum Waves Strategy
Neon Momentum Waves Strategy uses MACD histogram crossings to trade in both directions. The strategy goes long when the histogram crosses above the entry level (default zero) and goes short when it crosses below. Positions are closed when the histogram reaches configured exit levels.
Details
- Entry Criteria: MACD histogram crosses entry level.
- Long/Short: Both directions.
- Exit Criteria: Histogram crosses long/short exit levels.
- Stops: No.
- Default Values:
FastLength= 12SlowLength= 26SignalLength= 20EntryLevel= 0LongExitLevel= 11ShortExitLevel= -9CandleType= 1 minute
- Filters:
- Category: Momentum
- Direction: Both
- Indicators: MACD
- Stops: No
- Complexity: Basic
- Timeframe: Intraday (1m)
- 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>
/// Neon Momentum Waves strategy based on MACD histogram levels.
/// </summary>
public class NeonMomentumWavesStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<decimal> _entryLevel;
private readonly StrategyParam<decimal> _longExitLevel;
private readonly StrategyParam<decimal> _shortExitLevel;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHist;
private DateTimeOffset _lastSignal = DateTimeOffset.MinValue;
/// <summary>
/// MACD fast EMA length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// MACD slow EMA length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// MACD signal smoothing length.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// Histogram entry level.
/// </summary>
public decimal EntryLevel
{
get => _entryLevel.Value;
set => _entryLevel.Value = value;
}
/// <summary>
/// Histogram level to exit long position.
/// </summary>
public decimal LongExitLevel
{
get => _longExitLevel.Value;
set => _longExitLevel.Value = value;
}
/// <summary>
/// Histogram level to exit short position.
/// </summary>
public decimal ShortExitLevel
{
get => _shortExitLevel.Value;
set => _shortExitLevel.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="NeonMomentumWavesStrategy"/>.
/// </summary>
public NeonMomentumWavesStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast Length", "MACD fast EMA length", "MACD")
;
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow Length", "MACD slow EMA length", "MACD")
;
_signalLength = Param(nameof(SignalLength), 20)
.SetDisplay("Signal Length", "MACD signal smoothing", "MACD")
;
_entryLevel = Param(nameof(EntryLevel), 0m)
.SetDisplay("Entry Level", "Histogram entry threshold", "Parameters");
_longExitLevel = Param(nameof(LongExitLevel), 11m)
.SetDisplay("Long Exit Level", "Histogram level to exit longs", "Parameters");
_shortExitLevel = Param(nameof(ShortExitLevel), -9m)
.SetDisplay("Short Exit Level", "Histogram level to exit shorts", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHist = null;
_lastSignal = DateTimeOffset.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var typed = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (typed.Macd is not decimal macdLine || typed.Signal is not decimal signal)
return;
var hist = macdLine - signal;
if (_prevHist is null)
{
_prevHist = hist;
return;
}
var prev = _prevHist.Value;
var cooldown = TimeSpan.FromMinutes(360);
if (candle.OpenTime - _lastSignal >= cooldown)
{
if (prev <= EntryLevel && hist > EntryLevel && Position <= 0)
{
BuyMarket();
_lastSignal = candle.OpenTime;
}
else if (prev >= EntryLevel && hist < EntryLevel && Position > 0)
{
SellMarket();
_lastSignal = candle.OpenTime;
}
}
_prevHist = hist;
}
}
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 MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class neon_momentum_waves_strategy(Strategy):
def __init__(self):
super(neon_momentum_waves_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast Length", "MACD fast EMA length", "MACD")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow Length", "MACD slow EMA length", "MACD")
self._signal_length = self.Param("SignalLength", 20) \
.SetDisplay("Signal Length", "MACD signal smoothing", "MACD")
self._entry_level = self.Param("EntryLevel", 0.0) \
.SetDisplay("Entry Level", "Histogram entry threshold", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_hist = None
self._last_signal_ticks = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(neon_momentum_waves_strategy, self).OnReseted()
self._prev_hist = None
self._last_signal_ticks = 0
def OnStarted2(self, time):
super(neon_momentum_waves_strategy, self).OnStarted2(time)
self._prev_hist = None
self._last_signal_ticks = 0
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self._fast_length.Value
self._macd.Macd.LongMa.Length = self._slow_length.Value
self._macd.SignalMa.Length = self._signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._macd, self.OnProcess).Start()
def OnProcess(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
if not self._macd.IsFormed:
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
hist = float(macd_line) - float(signal_line)
if self._prev_hist is None:
self._prev_hist = hist
return
entry = float(self._entry_level.Value)
cooldown_ticks = TimeSpan.FromMinutes(360).Ticks
current_ticks = candle.OpenTime.Ticks
if current_ticks - self._last_signal_ticks >= cooldown_ticks:
if self._prev_hist <= entry and hist > entry and self.Position <= 0:
self.BuyMarket()
self._last_signal_ticks = current_ticks
elif self._prev_hist >= entry and hist < entry and self.Position > 0:
self.SellMarket()
self._last_signal_ticks = current_ticks
self._prev_hist = hist
def CreateClone(self):
return neon_momentum_waves_strategy()