Открыть на GitHub

Стратегия DLM v1.4

Обзор

Стратегия представляет собой порт на StockSharp советника MetaTrader 4 «DLM v1.4» от Alejandro Galindo. Оригинальный алгоритм использует фильтр Fisher Transform и схему усреднения по типу мартингейла: при движении цены против последней сделки открывается новая позиция, формируя сетку ордеров. Перенесённая версия сохраняет идеи управления капиталом и переписывает блоки сигналов, заявок и защит на высокоуровневом API StockSharp (подписка на свечи, привязка индикаторов, вспомогательные методы для рыночных и лимитных заявок).

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

  • Для выбранного таймфрейма обрабатываются только закрытые свечи, по которым считаются два индикатора: Fisher Transform и скользящая средняя (SMA), сглаживающая значения Fisher.
  • Направление сетки определяется сравнением линий. Если Fisher находится выше сглаженной версии, открывается корзина покупок, если ниже — корзина продаж. Параметр ReverseSignals меняет интерпретацию.
  • При появлении направления и включённом автотрейдинге (ManualTrading = false) первая позиция открывается рыночным ордером.
  • Пока корзина не закрыта, каждая неблагоприятная просадка на GridDistancePips пунктов относительно последней сделки приводит к добавлению новой позиции. Параметр UseLimitOrders задаёт режим: либо рыночная заявка на закрытии следующей свечи, либо заранее выставленный лимитный ордер на расстоянии одного шага сетки.
  • Размер каждой новой сделки растёт по мартингейлу: базовый объём умножается на 1.5, если MaxTrades > 12, иначе удваивается. Базовый объём может быть фиксированным (LotSize) или рассчитываться по балансу при включённом UseMoneyManagement.
  • После каждого заполнения пересчитываются общие уровни стоп-лосса и тейк-профита, чтобы вся корзина имела единые защитные уровни. При активном трейлинг-стопе уровень стопа подтягивается после прохождения ценой GridDistancePips + TrailingStopPips в сторону прибыли.

Защита счёта

  • Защита прибыли (SecureProfitProtection): при достижении OrdersToProtect открытых сделок плавающая прибыль (в валюте счёта) сравнивается с SecureProfit. При превышении порога корзина закрывается целиком.
  • Защита по equity (EquityProtection + EquityProtectionPercent): контролирует текущую стоимость портфеля и закрывает корзину, если equity опускается ниже заданного процента от значения в момент запуска стратегии.
  • Защита по просадке в деньгах (AccountMoneyProtection + AccountMoneyProtectionValue): прекращает торговлю, когда просадка относительно стартового equity превышает заданную сумму.
  • Ограничение по времени (OrdersLifeSeconds): задаёт максимальную «проживаемость» последней сделки; по истечении лимита все ордера закрываются, а цикл усреднения останавливается.
  • Фильтр пятницы (TradeOnFriday): запрещает запуск новых корзин по пятницам, если параметр отключён.

Все защитные выходы выполняются рыночными ордерами, что гарантирует исполнение. Активные лимитные заявки отменяются при срабатывании любого защитного условия или при сбросе сетки.

Параметры

Параметр Описание
TakeProfitPips Общая дистанция тейк-профита (в пунктах) для каждой сделки.
StopLossPips Начальный стоп-лосс (в пунктах) для каждой новой позиции.
TrailingStopPips Дистанция трейлинг-стопа после срабатывания триггера.
MaxTrades Максимальное число усреднений в одной корзине.
GridDistancePips Минимальное неблагоприятное смещение (в пунктах) перед добавлением сделки.
LotSize Базовый объём при отключённом управлении капиталом.
UseMoneyManagement Включает расчёт базового объёма по формуле оригинального советника.
RiskPercent Процент риска, используемый для динамического базового объёма.
AccountType Масштабирование динамического объёма (0 — стандартный лот, 1 — мини, 2 — микро).
SecureProfitProtection Включает защиту плавающей прибыли.
SecureProfit Порог (в валюте счёта), при достижении которого корзина закрывается.
OrdersToProtect Минимальное число открытых сделок для активации защиты прибыли.
EquityProtection Включает защиту по проценту equity.
EquityProtectionPercent Процент от стартового equity, ниже которого корзина закрывается.
AccountMoneyProtection Включает защиту по денежной просадке.
AccountMoneyProtectionValue Максимально допустимая просадка в валюте счёта.
TradeOnFriday Разрешение на запуск новых корзин в пятницу.
OrdersLifeSeconds Максимальное время жизни последней сделки (в секундах).
ReverseSignals Инвертирует направление сигналов Fisher.
UseLimitOrders Выбор между рыночными и лимитными добавлениями к позиции.
ManualTrading При значении true стратегия не открывает сделки автоматически.
CandleType Таймфрейм, используемый при расчёте индикаторов.
FisherLength Период расчёта Fisher Transform.
SignalSmoothing Период SMA, сглаживающей значения Fisher.
DefaultPipValue Запасное значение стоимости пункта для расчёта прибыли/убытка.

Примечания

  • Все комментарии в исходном коде выполнены на английском языке в соответствии с требованиями репозитория.
  • Стратегия работает только через высокоуровневые методы StockSharp (SubscribeCandles, Bind, BuyLimit, SellLimit и др.) и не обращается к буферам индикаторов напрямую.
  • Формула money management совпадает с оригинальной, при этом объёмы и цены дополнительно обрабатываются через Security.ShrinkVolume и Security.ShrinkPrice, чтобы учитывать спецификацию инструмента.
  • Поведение максимально приближено к версии MetaTrader, однако для совместимости с StockSharp выходы из корзины выполняются рыночными заявками.
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>
/// DLM v1 Grid strategy - EMA with RSI direction filter.
/// Buys when EMA is rising and RSI is above 50.
/// Sells when EMA is falling and RSI is below 50.
/// </summary>
public class Dlmv1GridStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevEma;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Dlmv1GridStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 14)
			.SetDisplay("EMA Period", "EMA lookback", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevEma = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevEma = ema;
			_hasPrev = true;
			return;
		}

		// EMA rising + RSI above 50 = buy
		if (ema > _prevEma && rsi > 50 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// EMA falling + RSI below 50 = sell
		else if (ema < _prevEma && rsi < 50 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevEma = ema;
	}
}