Macd Pattern Trader All
Strategy that opens positions on sharp MACD reversals. It looks for two large spikes around a small intermediate value of the MACD line. A sell is opened when the previous MACD value is positive and the current value drops deep into negative territory. A buy is opened on the opposite condition. Stop loss and take profit are derived from recent highs and lows.
The algorithm suits volatile markets where momentum quickly changes direction. It uses only market orders and calculates risk levels from candle history.
Details
- Entry Criteria: MACD spike ratio based on
RatioThreshold. - Long/Short: Both directions.
- Exit Criteria: Stop at recent extreme plus offset or opposite spike.
- Stops: Yes.
- Default Values:
FastEmaPeriod= 24SlowEmaPeriod= 13StopLossBars= 22TakeProfitBars= 32OffsetPoints= 40RatioThreshold= 5mCandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Pattern
- Direction: Both
- Indicators: MACD
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades sharp MACD reversals using recent highs and lows for risk.
/// </summary>
public class MacdPatternTraderAllStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _stopLossBars;
private readonly StrategyParam<int> _takeProfitBars;
private readonly StrategyParam<int> _offsetPoints;
private readonly StrategyParam<decimal> _ratioThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _macdPrev;
private decimal _macdPrev2;
private decimal _stopLossPrice;
private decimal _takeProfitPrice;
/// <summary>
/// Fast EMA period for MACD.
/// </summary>
public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
/// <summary>
/// Slow EMA period for MACD.
/// </summary>
public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
/// <summary>
/// Number of bars used to calculate stop loss.
/// </summary>
public int StopLossBars { get => _stopLossBars.Value; set => _stopLossBars.Value = value; }
/// <summary>
/// Number of bars used to calculate take profit.
/// </summary>
public int TakeProfitBars { get => _takeProfitBars.Value; set => _takeProfitBars.Value = value; }
/// <summary>
/// Offset in points added to stop loss.
/// </summary>
public int OffsetPoints { get => _offsetPoints.Value; set => _offsetPoints.Value = value; }
/// <summary>
/// Minimal ratio of MACD spikes to previous value.
/// </summary>
public decimal RatioThreshold { get => _ratioThreshold.Value; set => _ratioThreshold.Value = value; }
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="MacdPatternTraderAllStrategy"/> class.
/// </summary>
public MacdPatternTraderAllStrategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 24)
.SetDisplay("Fast EMA Period", "Period for fast EMA in MACD", "Indicators")
.SetOptimize(12, 40, 2);
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 13)
.SetDisplay("Slow EMA Period", "Period for slow EMA in MACD", "Indicators")
.SetOptimize(7, 26, 1);
_stopLossBars = Param(nameof(StopLossBars), 22)
.SetDisplay("Stop Loss Bars", "Bars to look back for stop loss", "Risk management")
.SetOptimize(10, 40, 1);
_takeProfitBars = Param(nameof(TakeProfitBars), 32)
.SetDisplay("Take Profit Bars", "Bars to look back for take profit", "Risk management")
.SetOptimize(10, 60, 2);
_offsetPoints = Param(nameof(OffsetPoints), 40)
.SetDisplay("Offset Points", "Point offset added to stop loss", "Risk management")
.SetOptimize(10, 60, 5);
_ratioThreshold = Param(nameof(RatioThreshold), 8m)
.SetDisplay("MACD Ratio", "Minimal ratio of surrounding MACD values", "Signals")
.SetOptimize(3m, 7m, 1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).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();
_macdPrev = 0m;
_macdPrev2 = 0m;
_stopLossPrice = 0m;
_takeProfitPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = FastEmaPeriod },
LongMa = { Length = SlowEmaPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal macdValue)
{
if (candle.State != CandleStates.Finished)
return;
// Manage existing position by fixed stop and target
if (Position > 0)
{
if (candle.LowPrice <= _stopLossPrice || candle.HighPrice >= _takeProfitPrice)
{
SellMarket(Position);
_stopLossPrice = 0m;
_takeProfitPrice = 0m;
}
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopLossPrice || candle.LowPrice <= _takeProfitPrice)
{
BuyMarket(Math.Abs(Position));
_stopLossPrice = 0m;
_takeProfitPrice = 0m;
}
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_macdPrev2 = _macdPrev;
_macdPrev = macdValue;
return;
}
var priceStep = Security?.PriceStep ?? 1m;
var offset = OffsetPoints * priceStep;
var stopDistance = offset * 2m;
var takeDistance = offset * 4m;
var macdCurr = macdValue;
var macdLast = _macdPrev;
var macdLast3 = _macdPrev2;
if (macdLast != 0m)
{
var ratio1 = Math.Abs(macdLast3 / macdLast);
var ratio2 = Math.Abs(macdCurr / macdLast);
if ((macdLast3 > 0m || macdCurr < 0m) && ratio1 >= RatioThreshold && ratio2 >= RatioThreshold && Position >= 0)
{
var sl = candle.ClosePrice + stopDistance;
var tp = candle.ClosePrice - takeDistance;
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_stopLossPrice = sl;
_takeProfitPrice = tp;
}
else if ((macdLast3 < 0m || macdCurr > 0m) && ratio1 >= RatioThreshold && ratio2 >= RatioThreshold && Position <= 0)
{
var sl = candle.ClosePrice - stopDistance;
var tp = candle.ClosePrice + takeDistance;
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_stopLossPrice = sl;
_takeProfitPrice = tp;
}
}
_macdPrev2 = _macdPrev;
_macdPrev = macdCurr;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class macd_pattern_trader_all_strategy(Strategy):
def __init__(self):
super(macd_pattern_trader_all_strategy, self).__init__()
self._fast_ema_period = self.Param("FastEmaPeriod", 24)
self._slow_ema_period = self.Param("SlowEmaPeriod", 13)
self._stop_loss_bars = self.Param("StopLossBars", 22)
self._take_profit_bars = self.Param("TakeProfitBars", 32)
self._offset_points = self.Param("OffsetPoints", 40)
self._ratio_threshold = self.Param("RatioThreshold", 8.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._macd_prev = 0.0
self._macd_prev2 = 0.0
self._stop_loss_price = 0.0
self._take_profit_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(macd_pattern_trader_all_strategy, self).OnStarted2(time)
self._macd_prev = 0.0
self._macd_prev2 = 0.0
self._stop_loss_price = 0.0
self._take_profit_price = 0.0
macd = MovingAverageConvergenceDivergence()
macd.ShortMa.Length = int(self._fast_ema_period.Value)
macd.LongMa.Length = int(self._slow_ema_period.Value)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(macd, self.ProcessCandle).Start()
def ProcessCandle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
macd_curr = float(macd_value)
close = float(candle.ClosePrice)
low = float(candle.LowPrice)
high = float(candle.HighPrice)
pos = float(self.Position)
# Manage existing position
if pos > 0:
if low <= self._stop_loss_price or high >= self._take_profit_price:
self.SellMarket(pos)
self._stop_loss_price = 0.0
self._take_profit_price = 0.0
elif pos < 0:
if high >= self._stop_loss_price or low <= self._take_profit_price:
self.BuyMarket(abs(pos))
self._stop_loss_price = 0.0
self._take_profit_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._macd_prev2 = self._macd_prev
self._macd_prev = macd_curr
return
price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
offset = float(self._offset_points.Value) * price_step
stop_distance = offset * 2.0
take_distance = offset * 4.0
macd_last = self._macd_prev
macd_last3 = self._macd_prev2
ratio_thresh = float(self._ratio_threshold.Value)
if macd_last != 0.0:
ratio1 = abs(macd_last3 / macd_last)
ratio2 = abs(macd_curr / macd_last)
pos = float(self.Position)
vol = float(self.Volume)
if (macd_last3 > 0.0 or macd_curr < 0.0) and ratio1 >= ratio_thresh and ratio2 >= ratio_thresh and pos >= 0:
sl = close + stop_distance
tp = close - take_distance
volume = vol + abs(pos)
self.SellMarket(volume)
self._stop_loss_price = sl
self._take_profit_price = tp
elif (macd_last3 < 0.0 or macd_curr > 0.0) and ratio1 >= ratio_thresh and ratio2 >= ratio_thresh and pos <= 0:
sl = close - stop_distance
tp = close + take_distance
volume = vol + abs(pos)
self.BuyMarket(volume)
self._stop_loss_price = sl
self._take_profit_price = tp
self._macd_prev2 = self._macd_prev
self._macd_prev = macd_curr
def OnReseted(self):
super(macd_pattern_trader_all_strategy, self).OnReseted()
self._macd_prev = 0.0
self._macd_prev2 = 0.0
self._stop_loss_price = 0.0
self._take_profit_price = 0.0
def CreateClone(self):
return macd_pattern_trader_all_strategy()