Открыть на GitHub

Стратегия Dematus

Общее описание

Стратегия Dematus является портом одноимённого советника MetaTrader 5. Она использует осциллятор DeMarker для поиска разворотных моментов и поддерживает пирамидинг с динамическим увеличением объёма. Работа ведётся по свечам типа CandleType, сигналы рассчитываются только на полностью сформированных барах.

На каждом завершённом баре анализируются два значения DeMarker: текущее и значение двух баров назад. Подъём показателя выше уровня 0.3 после нахождения ниже этой границы трактуется как восстановление бычьего импульса, а падение ниже 0.7 после нахождения выше этого уровня сигнализирует об усилении медведей. После первичного входа стратегия может добавлять позицию, если цена ушла от последней сделки не менее чем на DistancePips и индикатор вновь дал подтверждение.

Логика торговли

  • Первичный вход:
    • Покупка при пересечении уровня 0.3 снизу вверх (значение два бара назад < 0.3, текущее значение > 0.3) при отсутствии открытой позиции.
    • Продажа при пересечении уровня 0.7 сверху вниз (значение два бара назад > 0.7, текущее значение < 0.7) при отсутствии открытой позиции.
  • Добавление к позиции:
    • Стратегия хранит цену последней сделки. Если цена смещается против позиции как минимум на DistancePips (в пересчёте в цену) и возникает новый сигнал DeMarker в ту же сторону, подаётся заявка на добавление.
    • Объём очередной заявки равен предыдущему исполненному объёму, умноженному на VolumeMultiplier, затем приводится к шагу объёма инструмента и проверяется на соответствие ограничениям биржи. Это повторяет механику коэффициента лотов в исходном советнике.
  • Защита позиции:
    • На каждую новую позицию выставляется фиксированный стоп по параметру StopLossPips. При добавлении позиция переустанавливает общий уровень стопа.
    • Если TrailingStopPips > 0, стоп подтягивается вслед за ценой, когда прибыль превышает TrailingStopPips + TrailingStepPips, что эквивалентно логике трейлинга в MQL.
  • Защита капитала:
    • В состоянии без позиции формируется виртуальный уровень капитала Balance - VirtualStopEquity.
    • После роста капитала сверх TrailingStartEquity от начального баланса активируется трейлинг по капиталу: фиксируется максимум и создаётся плавающий порог Peak - TrailingEquity.
    • При падении капитала ниже виртуального порога все позиции закрываются рыночными ордерами.

Параметры

Параметр Описание
InitialVolume Базовый объём первой сделки и объём после полного закрытия позиции.
DemarkerLength Период расчёта индикатора DeMarker.
StopLossPips Расстояние до фиксированного стоп-лосса в пунктах (0 — без стопа).
TrailingStopPips Дистанция трейлинг-стопа в пунктах (0 — отключено).
TrailingStepPips Минимальный дополнительный ход в пунктах перед подтяжкой трейлинг-стопа; должен быть > 0 при включённом трейлинге.
DistancePips Минимальное отступление цены от последней сделки (в пунктах) перед добавлением позиции.
TrailingEquity Отступ между максимумом капитала и защитным порогом при трейлинге по equity.
VirtualStopEquity Буфер ниже баланса, определяющий стартовый уровень виртуального стопа при отсутствии позиции.
TrailingStartEquity Прирост капитала, необходимый для активации трейлинга по equity.
VolumeMultiplier Множитель, применяемый к объёму последней сделки при пирамидинге.
ResetEntryPrice При включении очищает сохранённую цену входа после выхода, блокируя немедленное добавление.
CandleType Тип/таймфрейм свечей, по которым выполняются расчёты и торговые решения.

Особенности реализации

  • Используется высокоуровневый API StockSharp. Подписка на свечи осуществляется через SubscribeCandles, а индикатор DeMarker подключается методом Bind, что позволяет получать готовые значения без прямого доступа к буферам.
  • Состояние индикатора хранится в трёх переменных, повторяющих обращение iDeMarkerGet(0) и iDeMarkerGet(2) в оригинальном коде.
  • Объёмы заявок приводятся к шагу объёма инструмента и проверяются на соответствие минимальным и максимальным ограничениям.
  • Контроль equity реализован через Portfolio.CurrentValue: при срабатывании виртуального стопа стратегия принудительно закрывает позицию.
  • Размер пункта вычисляется на основе Security.PriceStep. Для инструментов с точностью 3 или 5 знаков размер пункта умножается на 10, как и в MQL-версии.

Рекомендации по использованию

  • Необходимо, чтобы подключённый портфель предоставлял актуальные данные по equity, иначе механика защиты капитала не сработает.
  • Стратегия обрабатывает только завершённые свечи (CandleStates.Finished), что соответствует логике "нового бара" в исходном советнике.
  • Пороговые значения 0.3 / 0.7 заданы константами. При необходимости их можно изменить непосредственно в коде.
  • Стратегия подходит для реальной торговли и бэктестов. В режиме тестирования важно, чтобы симулятор портфеля возвращал значения equity, иначе блок защиты капитала будет неактивен.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Dematus strategy using DEMA crossover for trend detection.
/// </summary>
public class DematusStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public DematusStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast DEMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow DEMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = null;
		_prevSlow = null;

		var fast = new DoubleExponentialMovingAverage { Length = FastPeriod };
		var slow = new DoubleExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;

		_prevFast = fastVal;
		_prevSlow = slowVal;

		if (!prevAbove && currAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (prevAbove && !currAbove)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}