Martingale MACD Strategy
This strategy replicates the original MQL "MartGreg_1" expert in the StockSharp framework. It uses two Moving Average Convergence Divergence (MACD) indicators to search for reversals and applies a martingale rule for position sizing.
How it works
- The first MACD searches for local extremes on the last three finished candles.
- The second MACD compares the last two values to determine momentum direction.
- A long position is opened when the first MACD forms a valley and the second MACD decreases.
- A short position is opened when the first MACD forms a peak and the second MACD increases.
- After every losing trade the next order size is doubled up to the configured limit.
- Stop loss and take profit are set in absolute price points.
Parameters
Shape– divider for calculating initial volume from account balance.Doubling Count– maximum number of consecutive doublings after losses.Stop Loss– protective stop in points.Take Profit– profit target in points.MACD1 Fast/Slow– periods for the first MACD.MACD2 Fast/Slow– periods for the second MACD.Candle Type– timeframe for analysis.
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>
/// Martingale strategy based on dual MACD indicators.
/// Buys when MACD1 turns up and MACD2 turns down (divergence).
/// Sells on the opposite condition.
/// </summary>
public class MartingaleMacdStrategy : Strategy
{
private readonly StrategyParam<int> _macd1Fast;
private readonly StrategyParam<int> _macd1Slow;
private readonly StrategyParam<int> _macd2Fast;
private readonly StrategyParam<int> _macd2Slow;
private readonly StrategyParam<DataType> _candleType;
private decimal? _macd1Prev1;
private decimal? _macd1Prev2;
private decimal? _macd2Prev;
public int Macd1Fast { get => _macd1Fast.Value; set => _macd1Fast.Value = value; }
public int Macd1Slow { get => _macd1Slow.Value; set => _macd1Slow.Value = value; }
public int Macd2Fast { get => _macd2Fast.Value; set => _macd2Fast.Value = value; }
public int Macd2Slow { get => _macd2Slow.Value; set => _macd2Slow.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MartingaleMacdStrategy()
{
_macd1Fast = Param(nameof(Macd1Fast), 5)
.SetDisplay("MACD1 Fast", "Fast EMA for first MACD", "Indicators");
_macd1Slow = Param(nameof(Macd1Slow), 20)
.SetDisplay("MACD1 Slow", "Slow EMA for first MACD", "Indicators");
_macd2Fast = Param(nameof(Macd2Fast), 10)
.SetDisplay("MACD2 Fast", "Fast EMA for second MACD", "Indicators");
_macd2Slow = Param(nameof(Macd2Slow), 15)
.SetDisplay("MACD2 Slow", "Slow EMA for second MACD", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd1Prev1 = null;
_macd1Prev2 = null;
_macd2Prev = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macd1Prev1 = null;
_macd1Prev2 = null;
_macd2Prev = null;
var macd1 = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = Macd1Fast },
LongMa = { Length = Macd1Slow }
};
var macd2 = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = Macd2Fast },
LongMa = { Length = Macd2Slow }
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(macd1, macd2, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd1);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal macd1Val, decimal macd2Val)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_macd1Prev1.HasValue && _macd1Prev2.HasValue && _macd2Prev.HasValue)
{
var t0 = macd1Val;
var t1 = _macd1Prev1.Value;
var t2 = _macd1Prev2.Value;
var k0 = macd2Val;
var k1 = _macd2Prev.Value;
// MACD1 turns up (V-shape) and MACD2 turns down => buy
if (t0 > t1 && t1 < t2 && k1 > k0 && Position <= 0)
BuyMarket();
// MACD1 turns down (inverted V) and MACD2 turns up => sell
else if (t0 < t1 && t1 > t2 && k1 < k0 && Position >= 0)
SellMarket();
}
_macd1Prev2 = _macd1Prev1;
_macd1Prev1 = macd1Val;
_macd2Prev = macd2Val;
}
}
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 MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class martingale_macd_strategy(Strategy):
def __init__(self):
super(martingale_macd_strategy, self).__init__()
self._macd1_fast = self.Param("Macd1Fast", 5) \
.SetDisplay("MACD1 Fast", "Fast EMA for first MACD", "Indicators")
self._macd1_slow = self.Param("Macd1Slow", 20) \
.SetDisplay("MACD1 Slow", "Slow EMA for first MACD", "Indicators")
self._macd2_fast = self.Param("Macd2Fast", 10) \
.SetDisplay("MACD2 Fast", "Fast EMA for second MACD", "Indicators")
self._macd2_slow = self.Param("Macd2Slow", 15) \
.SetDisplay("MACD2 Slow", "Slow EMA for second MACD", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
@property
def macd1_fast(self):
return self._macd1_fast.Value
@property
def macd1_slow(self):
return self._macd1_slow.Value
@property
def macd2_fast(self):
return self._macd2_fast.Value
@property
def macd2_slow(self):
return self._macd2_slow.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(martingale_macd_strategy, self).OnReseted()
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
def OnStarted2(self, time):
super(martingale_macd_strategy, self).OnStarted2(time)
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
macd1 = MovingAverageConvergenceDivergence()
macd1.ShortMa.Length = self.macd1_fast
macd1.LongMa.Length = self.macd1_slow
macd2 = MovingAverageConvergenceDivergence()
macd2.ShortMa.Length = self.macd2_fast
macd2.LongMa.Length = self.macd2_slow
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(macd1, macd2, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd1)
self.DrawOwnTrades(area)
def process_candle(self, candle, macd1_val, macd2_val):
if candle.State != CandleStates.Finished:
return
macd1_val = float(macd1_val)
macd2_val = float(macd2_val)
if self._macd1_prev1 is not None and self._macd1_prev2 is not None and self._macd2_prev is not None:
t0 = macd1_val
t1 = self._macd1_prev1
t2 = self._macd1_prev2
k0 = macd2_val
k1 = self._macd2_prev
if t0 > t1 and t1 < t2 and k1 > k0 and self.Position <= 0:
self.BuyMarket()
elif t0 < t1 and t1 > t2 and k1 < k0 and self.Position >= 0:
self.SellMarket()
self._macd1_prev2 = self._macd1_prev1
self._macd1_prev1 = macd1_val
self._macd2_prev = macd2_val
def CreateClone(self):
return martingale_macd_strategy()