Стратегия переносит советник «MACD Four Colors 2 Martingale» с платформы MetaTrader в StockSharp и сохраняет оригинальную логику, основанную на цветовом анализе MACD и мартингейле при наборе позиции.
Общее описание
Базовый индикатор раскрашивает гистограмму MACD в пять цветов. В стандартной «новой» схеме цвет меняется в зависимости от того, растёт или падает линия MACD и находится ли она выше либо ниже нулевой линии. Советник открывает позицию, когда палитра меняется с серебристой на жёлтую (MACD остаётся ниже нуля и снова начинает падать) или с красной на синюю (MACD выше нуля и разворачивается вниз). Версия для StockSharp восстанавливает эти переходы по значениям MACD и повторяет сигналы.
Одновременно допускается только одна направленная серия сделок. Новый вход разрешён, если цена улучшает среднюю цену набранной серии: ниже для покупок и выше для продаж. Каждый следующий ордер умножает объём предыдущего заполненного ордера на коэффициент LotCoefficient, что реализует мартингейл из оригинального кода.
Торговые правила
Логика индикатора: используется MovingAverageConvergenceDivergenceSignal с классическими параметрами 12/26/9.
Восстановление цветов: стратегия сравнивает два последних значения MACD. Растущий отрицательный MACD соответствует цвету 1 (серебро), растущий положительный — цвету 2 (красный), падающий положительный — цвету 3 (синий), падающий отрицательный — цвету 4 (жёлтый).
Вход в лонг: сигнал формируется, когда цвета переходят с 1 на 4, а MACD на предыдущем баре остаётся ниже нуля. Сделка исполняется только при отсутствии коротких позиций и если цена ниже всех предыдущих входов в лонг.
Вход в шорт: сигнал возникает при переходе цветов с 2 на 3 и положительном MACD на предыдущем баре. Сделка разрешена только при отсутствии длинных позиций и если цена выше всех открытых шортов.
Управление объёмом: первый ордер использует InitialVolume. Каждый следующий ордер внутри серии умножает объём на LotCoefficient. Если коэффициент ≤ 0, усиление отключается.
Контроль прибыли и убытков: плавающая прибыль пересчитывается на каждом закрытом баре. Достижение TargetProfit закрывает все позиции и сбрасывает цикл. Пробой MaxDrawdown (порог убытка) также приводит к полному выходу и перезапуску. Поддерживаются отрицательные пороги по аналогии с MQL-реализацией.
Выход из позиции: кроме денежных целей автоматических стопов нет. Позиции удерживаются, пока не сработают ограничители или пока пользователь не вмешается.
Параметры
CandleType(DataType, по умолчанию 1h) — таймфрейм для расчёта MACD.
InitialVolume(decimal, по умолчанию 1) — объём первого ордера в серии.
LotCoefficient(decimal, по умолчанию 2) — множитель объёма для следующего ордера в мартингейле.
MaxDrawdown(decimal, по умолчанию 50) — порог плавающего убытка в деньгах, при достижении которого закрываются все позиции. Положительное значение контролирует -MaxDrawdown, отрицательное трактуется напрямую.
TargetProfit(decimal, по умолчанию 150) — цель по плавающей прибыли в деньгах. Отрицательное значение инвертирует сравнение, как и в версии MQL.
FastEmaPeriod(int, по умолчанию 12) — период быстрой EMA в MACD.
SlowEmaPeriod(int, по умолчанию 26) — период медленной EMA в MACD.
SignalPeriod(int, по умолчанию 9) — период сигнальной EMA.
Рекомендации по использованию
Стратегии требуется инструмент с заданными PriceStep и StepPrice, поскольку не реализован независимый расчёт плавающего PnL.
Мартингейл быстро наращивает объём позиции, поэтому заранее проверьте лимиты риска и маржинальные требования брокера.
Для визуального контроля используйте создаваемую область графика: на ней отображаются свечи, MACD и сделки стратегии.
Фильтры каталога
Категория: тренд / усреднение по импульсу
Направление: обе стороны (лонг и шорт)
Индикаторы: MACD
Стопы: только денежные цели
Таймфрейм: настраиваемый (по умолчанию 1h)
Сложность: средняя
Риск: высокий из-за мартингейла
Автоматизация: полная
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD histogram crossover with martingale volume scaling.
/// Based on the MACD Four Colors 2 Martingale expert advisor.
/// </summary>
public class MacdFourColors2MartingaleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _lotCoefficient;
private readonly StrategyParam<int> _maxMartingale;
private MovingAverageConvergenceDivergence _macd;
private readonly System.Collections.Generic.Queue<decimal> _macdHistory = new();
private decimal? _prevHistogram;
private decimal _currentVolume;
private int _consecutiveLosses;
private decimal _entryPrice;
/// <summary>
/// Type of candles used for MACD analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Period of the fast EMA inside MACD.
/// </summary>
public int FastEmaPeriod
{
get => _fastEmaPeriod.Value;
set => _fastEmaPeriod.Value = value;
}
/// <summary>
/// Period of the slow EMA inside MACD.
/// </summary>
public int SlowEmaPeriod
{
get => _slowEmaPeriod.Value;
set => _slowEmaPeriod.Value = value;
}
/// <summary>
/// Period of the signal line.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Multiplier applied after a losing trade.
/// </summary>
public decimal LotCoefficient
{
get => _lotCoefficient.Value;
set => _lotCoefficient.Value = value;
}
/// <summary>
/// Maximum number of martingale steps before resetting.
/// </summary>
public int MaxMartingale
{
get => _maxMartingale.Value;
set => _maxMartingale.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public MacdFourColors2MartingaleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for MACD analysis", "General");
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 20)
.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
.SetGreaterThanZero();
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
.SetGreaterThanZero();
_signalPeriod = Param(nameof(SignalPeriod), 12)
.SetDisplay("Signal Period", "Signal line smoothing period", "Indicators")
.SetGreaterThanZero();
_lotCoefficient = Param(nameof(LotCoefficient), 1.5m)
.SetDisplay("Lot Coefficient", "Multiplier after a loss", "Money Management")
.SetGreaterThanZero();
_maxMartingale = Param(nameof(MaxMartingale), 5)
.SetDisplay("Max Martingale", "Maximum consecutive doublings", "Money Management")
.SetGreaterThanZero();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHistogram = null;
_currentVolume = Volume > 0 ? Volume : 1;
_consecutiveLosses = 0;
_entryPrice = 0;
_macdHistory.Clear();
_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);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal macdValue)
{
if (candle.State != CandleStates.Finished)
return;
_macdHistory.Enqueue(macdValue);
while (_macdHistory.Count > SignalPeriod)
_macdHistory.Dequeue();
if (!_macd.IsFormed || _macdHistory.Count < SignalPeriod)
{
_prevHistogram = null;
return;
}
// Calculate signal line (SMA of MACD)
var sum = 0m;
var history = _macdHistory.ToArray();
foreach (var v in history)
sum += v;
var signalValue = sum / history.Length;
var histogram = macdValue - signalValue;
if (_prevHistogram is null)
{
_prevHistogram = histogram;
return;
}
var crossUp = _prevHistogram < 0 && histogram >= 0;
var crossDown = _prevHistogram >= 0 && histogram < 0;
if (crossUp)
{
// Check for loss on closing short
if (Position < 0)
{
var pnl = _entryPrice - candle.ClosePrice;
if (pnl < 0)
{
_consecutiveLosses++;
if (_consecutiveLosses <= MaxMartingale)
_currentVolume *= LotCoefficient;
}
else
{
_consecutiveLosses = 0;
_currentVolume = Volume > 0 ? Volume : 1;
}
BuyMarket(Math.Abs(Position) + _currentVolume);
_entryPrice = candle.ClosePrice;
}
else if (Position == 0)
{
BuyMarket(_currentVolume);
_entryPrice = candle.ClosePrice;
}
}
else if (crossDown)
{
// Check for loss on closing long
if (Position > 0)
{
var pnl = candle.ClosePrice - _entryPrice;
if (pnl < 0)
{
_consecutiveLosses++;
if (_consecutiveLosses <= MaxMartingale)
_currentVolume *= LotCoefficient;
}
else
{
_consecutiveLosses = 0;
_currentVolume = Volume > 0 ? Volume : 1;
}
SellMarket(Math.Abs(Position) + _currentVolume);
_entryPrice = candle.ClosePrice;
}
else if (Position == 0)
{
SellMarket(_currentVolume);
_entryPrice = candle.ClosePrice;
}
}
_prevHistogram = histogram;
}
/// <inheritdoc />
protected override void OnReseted()
{
_macd = null;
_prevHistogram = null;
_currentVolume = 0;
_consecutiveLosses = 0;
_entryPrice = 0;
_macdHistory.Clear();
base.OnReseted();
}
}