Открыть на GitHub

Стратегия Simple 2 MA I

Обзор

Simple 2 MA I — трендовая стратегия, повторяющая ключевую логику одноимённого советника MetaTrader. Для определения направления используется пара линейных взвешенных скользящих средних (LWMA), рассчитанных по типичной цене. Дополнительные фильтры по импульсу (Rate of Change) и MACD отсекают слабые сигналы. Управление риском включает автоматические стоп-заявки, перевод позиции в безубыток и трал по экстремумам свечей.

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

Условия для покупки

  1. Быстрая LWMA расположена выше медленной, что подтверждает восходящий тренд.
  2. Минимум свечи два бара назад ниже максимума предыдущей свечи — формируется новая бычья структура.
  3. Минимум одно из трёх последних значений импульса превышает заданный порог.
  4. Линия MACD находится выше сигнальной линии.
  5. Текущий объём позиции по модулю меньше параметра Max Net Volume.

При выполнении условий стратегия закрывает короткие позиции (если есть) и открывает длинную по рынку.

Условия для продажи

  1. Быстрая LWMA ниже медленной, что указывает на нисходящий тренд.
  2. Минимум предыдущей свечи ниже максимума свечи два бара назад, формируя медвежью структуру.
  3. Абсолютное значение хотя бы одного из трёх последних импульсов превышает порог.
  4. Линия MACD ниже сигнальной линии.
  5. Модуль позиции меньше Max Net Volume.

Когда условия выполнены, стратегия закрывает длинные позиции (если есть) и продаёт по рынку.

Управление риском

  • Стоп-лосс / тейк-профит: по желанию выставляются на фиксированном расстоянии от цены входа (в пунктах).
  • Перевод в безубыток: при достижении заданной прибыли стоп переносится на уровень входа с заданным смещением.
  • Свечной трал: после активации стоп следует за минимумами/максимумами завершённых свечей с заданным запасом.
  • При закрытии позиции все защитные заявки автоматически снимаются.

Параметры

Параметр Описание Значение по умолчанию
Candle Type Таймфрейм для расчёта индикаторов. Свечи 15 минут
Fast LWMA Период быстрой LWMA. 6
Slow LWMA Период медленной LWMA. 85
Momentum Length Глубина расчёта индикатора Rate of Change. 14
Momentum Threshold Минимальное абсолютное значение импульса для подтверждения. 0.3
MACD Fast Период быстрой EMA в MACD. 12
MACD Slow Период медленной EMA в MACD. 26
MACD Signal Период сигнальной EMA в MACD. 9
Use Stop-Loss Включить постановку стоп-лосса. true
Stop-Loss (points) Расстояние стоп-лосса от цены входа, в пунктах. 20
Use Take-Profit Включить постановку тейк-профита. true
Take-Profit (points) Расстояние тейк-профита от цены входа, в пунктах. 50
Use Break-Even Включить перевод позиции в безубыток. true
Break-Even Trigger Прибыль в пунктах, необходимая для перевода в безубыток. 30
Break-Even Offset Смещение в пунктах при переносе стопа. 30
Use Candle Trailing Включить тралинг по свечам. true
Trailing Activation Прибыль в пунктах до начала трала. 40
Trailing Padding Запас в пунктах от экстремума свечи. 10
Max Net Volume Максимально допустимый объём позиции по модулю. 1

Примечания

  • Все расстояния выражены в шагах цены инструмента; стратегия умножает параметры на реальный тик.
  • Таймфрейм по умолчанию соответствует оригинальному советнику, но при необходимости может быть изменён.
  • Обрабатываются только завершённые свечи, что обеспечивает идентичное поведение исходной версии.
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;

public class Simple2MaIStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public Simple2MaIStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}