Открыть на GitHub

Стратегия прорыва с фиксацией прибыли

Стратегия повторяет советник MetaTrader «take-profit» и отслеживает четыре подряд идущие свечи со строго возрастающими или убывающими максимумами и ценами открытия. Если свечи формируют возрастающую последовательность, открывается покупка по рынку, при зеркальном условии — продажа. Управление сделками реализовано через глобальную цель по прибыли, трейлинг-стоп с возможностью частичной фиксации позиции и настраиваемый стоп-лосс в шагах цены.

По умолчанию анализируются минутные свечи, но тип свечей и смещения можно менять под конкретный инструмент. Объём позиций задаётся фиксированным числом лотов либо вычисляется от стоимости портфеля и процента риска. При каждом продвижении трейлинг-стопа стратегия может закрывать половину оставшейся позиции, фиксируя прибыль и оставляя «хвост» для дальнейшего движения.

Когда рыночная стоимость портфеля превышает стартовый капитал на величину ProfitTarget, стратегия немедленно закрывает позицию и отменяет активные заявки, полностью повторяя логику оригинального советника. Проверка корректности процентов риска гарантирует, что рассчитанный объём соответствует шагу объёма инструмента.

Подробности

  • Входы:
    • Покупка: четыре отслеживаемые свечи показывают строго возрастающие максимумы и цены открытия.
    • Продажа: четыре свечи демонстрируют строго убывающие максимумы и цены открытия.
  • Управление позицией:
    • Необязательный стоп-лосс устанавливается на величину StopLossPoints от цены входа.
    • Трейлинг-стоп подтягивается, когда цена проходит расстояние TrailingStopPoints от точки входа.
    • При каждом обновлении трейлинг-стопа, если включена опция PartialClose, закрывается половина текущего объёма с учётом шага объёма.
  • Цель по счёту: при выполнении условия equity ≥ initial equity + ProfitTarget все позиции закрываются, заявки отменяются.
  • Риск-менеджмент:
    • Фиксированный объём использует параметр Lots (или Volume, если задан базовым классом).
    • Процентный режим рассчитывает объём как equity * RiskPercent / max(stopDistance, price) и нормализует его по шагу объёма.
  • Значения по умолчанию:
    • Shift1 = 0, Shift2 = 1, Shift3 = 2, Shift4 = 3.
    • TrailingStopPoints = 1, StopLossPoints = 0, ProfitTarget = 1 (валюта счёта).
    • Lots = 1, RiskPercent = 1, MaxOrders = 1.
    • CandleType = минутный таймфрейм.
  • Лучше всего работает на трендовых фьючерсах, основных валютных парах и ликвидных криптоактивах с устойчивыми импульсами.
  • Плюсы: быстрый отклик на импульс, цель по счёту, частичное закрытие и понятный контроль риска.
  • Минусы: чувствительность к боковику, требовательность к корректным шагам цены и объёма, расчёт ведётся в неттинговом режиме (одна агрегированная позиция).

Параметры

Имя Описание
Shift1Shift4 Индексы свечей, которые сравниваются при поиске прорыва.
TrailingStopPoints Дистанция трейлинг-стопа в шагах цены.
StopLossPoints Начальный стоп-лосс в шагах цены; 0 — без стоп-лосса.
ProfitTarget Цель по прибыли в валюте счёта, после достижения которой позиции закрываются.
Lots Фиксированный объём при отключённом риск-менеджменте.
RiskManagement Переключатель процентного режима расчёта объёма.
RiskPercent Процент от капитала, используемый для расчёта объёма при активном риск-менеджменте.
PartialClose Включает частичное закрытие при продвижении трейлинг-стопа.
MaxOrders Максимальное количество базовых единиц (ограничение нетто-позиции).
CandleType Тип свечей для анализа.

Рекомендации

  1. Подбирайте смещения Shift в зависимости от волатильности инструмента: большие значения анализируют более длинные импульсы.
  2. Согласовывайте TrailingStopPoints с шагом цены, чтобы избежать хаотичных срабатываний.
  3. При использовании процентного режима обязательно задавайте StopLossPoints, чтобы риск на сделку был контролируемым.
  4. После достижения цели по прибыли стратегия прекращает торговлю до ручного перезапуска — это особенность оригинального советника.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Take Profit Breakout: Consecutive candle breakout with EMA filter and ATR stops.
/// </summary>
public class TakeProfitBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public TakeProfitBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 30)
			.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}