IMACD Sniper
IMACD Sniper combines MACD crossovers with an EMA trend filter, volume confirmation, and strong candle patterns. Dynamic take profit and stop loss are based on the recent average range.
Details
- Data: Price candles.
- Entry Criteria:
- Long: MACD line crosses above signal line, price above EMA, MACD delta > min delta, both lines far from zero, volume above average, strong bullish candle.
- Short: MACD line crosses below signal line, price below EMA, MACD delta > min delta, both lines far from zero, volume above average, strong bearish candle.
- Exit Criteria: Opposite MACD cross or reaching take profit / stop loss.
- Stops: Dynamic take profit and stop loss based on average range.
- Default Values:
FastLength= 12SlowLength= 26SignalLength= 9MacdDeltaMin= 0.03MacdZeroLimit= 0.05RangeLength= 14RangeMultiplierTp= 4.0RangeMultiplierSl= 1.5EmaLength= 20CandleType= tf(1m)
- Filters:
- Category: Trend
- Direction: Long & Short
- Indicators: MACD, EMA, Volume
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Any
- 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>
/// IMACD Sniper strategy combining MACD crossovers, EMA trend filter,
/// volume confirmation and dynamic take-profit/stop-loss.
/// </summary>
public class ImacdSniperStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<decimal> _macdDeltaMin;
private readonly StrategyParam<decimal> _macdZeroLimit;
private readonly StrategyParam<int> _rangeLength;
private readonly StrategyParam<decimal> _rangeMultiplierTp;
private readonly StrategyParam<decimal> _rangeMultiplierSl;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMacd;
private decimal _prevSignal;
private decimal _entryPrice;
/// <summary>
/// MACD fast length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// MACD slow length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// MACD signal length.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// Minimum MACD delta for entry.
/// </summary>
public decimal MacdDeltaMin
{
get => _macdDeltaMin.Value;
set => _macdDeltaMin.Value = value;
}
/// <summary>
/// Minimum distance from zero line.
/// </summary>
public decimal MacdZeroLimit
{
get => _macdZeroLimit.Value;
set => _macdZeroLimit.Value = value;
}
/// <summary>
/// Length for average range calculation.
/// </summary>
public int RangeLength
{
get => _rangeLength.Value;
set => _rangeLength.Value = value;
}
/// <summary>
/// Take-profit multiplier of the range.
/// </summary>
public decimal RangeMultiplierTp
{
get => _rangeMultiplierTp.Value;
set => _rangeMultiplierTp.Value = value;
}
/// <summary>
/// Stop-loss multiplier of the range.
/// </summary>
public decimal RangeMultiplierSl
{
get => _rangeMultiplierSl.Value;
set => _rangeMultiplierSl.Value = value;
}
/// <summary>
/// EMA length.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ImacdSniperStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast Length", "MACD fast period", "MACD")
.SetOptimize(6, 24, 2);
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow Length", "MACD slow period", "MACD")
.SetOptimize(20, 40, 2);
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal Length", "MACD signal smoothing", "MACD")
.SetOptimize(5, 15, 1);
_macdDeltaMin = Param(nameof(MacdDeltaMin), 0.03m)
.SetGreaterThanZero()
.SetDisplay("Min MACD Delta", "Minimum MACD difference for entry", "Filters")
.SetOptimize(0.01m, 0.1m, 0.01m);
_macdZeroLimit = Param(nameof(MacdZeroLimit), 0.05m)
.SetGreaterThanZero()
.SetDisplay("MACD Zero Limit", "Minimum distance from zero for entry", "Filters")
.SetOptimize(0.01m, 0.1m, 0.01m);
_rangeLength = Param(nameof(RangeLength), 14)
.SetGreaterThanZero()
.SetDisplay("Range Length", "Number of candles for range average", "Risk")
.SetOptimize(5, 30, 1);
_rangeMultiplierTp = Param(nameof(RangeMultiplierTp), 4m)
.SetGreaterThanZero()
.SetDisplay("Range Multiplier TP", "TP multiplier of range", "Risk")
.SetOptimize(1m, 6m, 1m);
_rangeMultiplierSl = Param(nameof(RangeMultiplierSl), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Range Multiplier SL", "SL multiplier of range", "Risk")
.SetOptimize(1m, 3m, 0.5m);
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Trend")
.SetOptimize(10, 50, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_prevMacd = default;
_prevSignal = default;
_entryPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = FastLength }, LongMa = { Length = SlowLength } },
SignalMa = { Length = SignalLength }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
var macdArea = CreateChartArea();
DrawIndicator(macdArea, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdTyped)
return;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
if (emaValue.IsEmpty)
return;
var ema = emaValue.ToDecimal();
var macdDelta = Math.Abs(macd - signal);
var longCondition = _prevMacd != 0 && _prevMacd <= _prevSignal && macd > signal && candle.ClosePrice > ema && macdDelta > MacdDeltaMin;
var shortCondition = _prevMacd != 0 && _prevMacd >= _prevSignal && macd < signal && candle.ClosePrice < ema && macdDelta > MacdDeltaMin;
if (longCondition && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (shortCondition && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
else if (Position > 0 && _prevMacd >= _prevSignal && macd < signal)
{
SellMarket();
}
else if (Position < 0 && _prevMacd <= _prevSignal && macd > signal)
{
BuyMarket();
}
_prevMacd = macd;
_prevSignal = signal;
}
}
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 ExponentialMovingAverage, MovingAverageConvergenceDivergenceSignal, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class imacd_sniper_strategy(Strategy):
"""
IMACD Sniper: MACD crossover with EMA trend filter.
Enters when MACD crosses signal with sufficient delta and price above/below EMA.
"""
def __init__(self):
super(imacd_sniper_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("MACD Fast Length", "MACD fast period", "MACD")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("MACD Slow Length", "MACD slow period", "MACD")
self._signal_length = self.Param("SignalLength", 9) \
.SetDisplay("MACD Signal Length", "MACD signal smoothing", "MACD")
self._macd_delta_min = self.Param("MacdDeltaMin", 0.03) \
.SetDisplay("Min MACD Delta", "Minimum MACD difference for entry", "Filters")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period", "Trend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_macd = 0.0
self._prev_signal = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(imacd_sniper_strategy, self).OnReseted()
self._prev_macd = 0.0
self._prev_signal = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(imacd_sniper_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_length.Value
macd.Macd.LongMa.Length = self._slow_length.Value
macd.SignalMa.Length = self._signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, macd_value, ema_value):
if candle.State != CandleStates.Finished:
return
macd_val = macd_value.Macd
signal_val = macd_value.Signal
if macd_val is None or signal_val is None:
return
if ema_value.IsEmpty:
return
macd = float(macd_val)
signal = float(signal_val)
ema = float(IndicatorHelper.ToDecimal(ema_value))
close = float(candle.ClosePrice)
macd_delta = abs(macd - signal)
delta_min = self._macd_delta_min.Value
long_cond = (self._prev_macd != 0 and self._prev_macd <= self._prev_signal
and macd > signal and close > ema and macd_delta > delta_min)
short_cond = (self._prev_macd != 0 and self._prev_macd >= self._prev_signal
and macd < signal and close < ema and macd_delta > delta_min)
if long_cond and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
elif short_cond and self.Position >= 0:
self.SellMarket()
self._entry_price = close
elif self.Position > 0 and self._prev_macd >= self._prev_signal and macd < signal:
self.SellMarket()
elif self.Position < 0 and self._prev_macd <= self._prev_signal and macd > signal:
self.BuyMarket()
self._prev_macd = macd
self._prev_signal = signal
def CreateClone(self):
return imacd_sniper_strategy()