Открыть на GitHub

3874 Trendcapture

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

Trendcapture — это перенос советника MetaTrader MQL/7772/Trendcapture.mq4 на высокоуровневый API StockSharp. Исходный алгоритм отслеживает направление Parabolic SAR и открывает сделки только тогда, когда ADX показывает слабый тренд. После закрытия позиции он выбирает направление следующей сделки по результату предыдущей, а при достижении небольшой прибыли переносит стоп-лосс в точку безубытка.

Версия для StockSharp сохраняет эти особенности, используя связки индикаторов и вспомогательные методы регистрации заявок. Сигналы рассчитываются исключительно на закрытых свечах выбранного таймфрейма.

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

  1. Индикаторы
    • Parabolic SAR (ParabolicSar) с настраиваемым шагом и максимальным ускорением.
    • Average Directional Index (AverageDirectionalIndex), откуда берётся основное значение ADX.
  2. Вход в позицию
    • Открытой может быть только одна позиция.
    • Длинная позиция открывается, если:
      • Текущая «желаемая» ориентация (по итогу последней сделки) — покупка.
      • Свеча закрылась выше значения SAR.
      • ADX ниже 20, что соответствует условию слабого тренда в оригинале.
    • Короткая позиция строится зеркально: желаемое направление — продажа, закрытие ниже SAR, ADX ниже 20.
  3. Управление позицией
    • После исполнения заявки выставляются стоп-лосс и тейк-профит на расстояниях StopLossPoints и TakeProfitPoints, пересчитанных через шаг цены инструмента.
    • Как только плавающая прибыль достигает GuardPoints, стоп переносится на цену входа — аналог OrderModify из MQL.
    • При закрытии позиции направление обновляется: прибыльная сделка сохраняет ориентацию, убыточная или нулевая — меняет её на противоположную.

Параметры

Параметр Описание Значение по умолчанию
CandleType Тип свечей для расчёта сигналов. Часовые свечи
SarStep Начальный шаг Parabolic SAR. 0.02
SarMax Максимальное ускорение Parabolic SAR. 0.2
AdxPeriod Период сглаживания ADX. 14
TakeProfitPoints Дистанция до тейк-профита в шагах цены. 180
StopLossPoints Дистанция до стоп-лосса в шагах цены. 50
GuardPoints Количество пунктов до перевода стопа в безубыток. 5
MaximumRisk Коэффициент масштабирования объёма (значение 0.03 повторяет размер лота из оригинала). 0.03

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

  • Убедитесь, что для инструмента задан PriceStep или хотя бы MinStep, иначе пересчёт пунктов в цены будет некорректным.
  • Свойство Volume задаёт базовый объём при MaximumRisk = 0.03. Увеличение коэффициента риска пропорционально увеличит подаваемый объём.
  • Стратегия работает только рыночными ордерами и сразу выставляет защитные заявки, поэтому при отсутствии позиции в стакане не остаётся «забытых» ордеров.
  • Перенос стопа в безубыток реализован через отмену текущей стоп-заявки и постановку новой по цене входа, что полностью повторяет поведение эксперта MetaTrader.

Файлы

  • CS/TrendcaptureStrategy.cs — реализация стратегии на C#.
  • README.md — описание на английском языке.
  • README_zh.md — перевод на китайский язык.
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>
/// Trendcapture strategy - EMA trend with ADX strength filter.
/// Buys when close is above EMA and ADX indicates trending.
/// Sells when close is below EMA and ADX indicates trending.
/// </summary>
public class TrendcaptureStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _adxPeriod;
	private readonly StrategyParam<decimal> _adxThreshold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevEma;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
	public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TrendcaptureStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA lookback", "Indicators");
		_adxPeriod = Param(nameof(AdxPeriod), 14)
			.SetDisplay("ADX Period", "ADX lookback", "Indicators");
		_adxThreshold = Param(nameof(AdxThreshold), 30m)
			.SetDisplay("ADX Threshold", "Minimum ADX for trending", "Levels");
		_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(); _prevClose = 0m; _prevEma = 0m; _hasPrev = false; }

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

		var fast = new ExponentialMovingAverage { Length = EmaPeriod };
		var slow = new ExponentialMovingAverage { Length = EmaPeriod * 3 };

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

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

		if (!_hasPrev) { _prevClose = fast; _prevEma = slow; _hasPrev = true; return; }

		if (_prevClose <= _prevEma && fast > slow && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		else if (_prevClose >= _prevEma && fast < slow && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevClose = fast; _prevEma = slow;
	}
}