Открыть на GitHub

Стратегия «Фиксация equity по проценту»

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

  • Категория: управление рисками / автоматизация на уровне счёта.
  • Оригинальный источник: советник MQL5 «Close by Equity Percent» (#20880).
  • Назначение: отслеживать, насколько текущая стоимость портфеля (equity) превосходит последнюю фиксированную величину баланса, и при достижении заданного коэффициента закрывать все открытые позиции.
  • Инструменты: любые бумаги, которые уже торгуются другими стратегиями или вручную в рамках выбранного портфеля.

Основная идея

Первоначальный советник сравнивал AccountEquity с AccountBalance. Баланс меняется только после полного закрытия позиций, поэтому при достижении порога Balance * EquityPercentFromBalance все сделки фиксируются. Перенос в StockSharp сохраняет эту защиту прибыли, используя высокоуровневый API стратегии.

Как работает стратегия

  1. При запуске фиксируется текущее значение портфеля — это «баланс» в состоянии без позиций.
  2. Стратегия подписывается на минутные свечи (параметр CandleType) по выбранному инструменту. Поток свечей используется исключительно как таймер проверки equity.
  3. На каждой завершённой свече:
    • Если портфель плоский (нет позиций), эталон баланса обновляется до текущего значения портфеля.
    • Текущее equity (Portfolio.CurrentValue) сравнивается с balanceSnapshot * EquityPercentFromBalance.
    • Если условие выполняется, стратегия вызывает ClosePosition(position.Security) для каждой открытой позиции и фиксирует результат.
  4. После закрытия всех позиций баланс снова обновляется, и цикл ожидания повторяется.

Параметры

Имя Тип Значение по умолчанию Описание
EquityPercentFromBalance decimal 1.20 Коэффициент роста equity относительно баланса, при котором нужно закрыть все позиции. Значение 1.20 означает фиксацию прибыли при увеличении equity до 120% от последнего баланса.
CandleType DataType Свечи таймфрейма 1 минута Поток данных, запускающий периодические проверки equity. Можно изменить на более подходящий таймфрейм.

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

  • Для закрытия позиций используется Strategy.ClosePosition(Security) — прямой аналог цикла PositionClose из исходного MQL-кода.
  • Снимок баланса обновляется только после полного закрытия всех позиций, что соответствует поведению AccountBalance в MetaTrader.
  • Стратегия не открывает сделки — она контролирует уже существующие позиции и закрывает все бумаги в выбранном портфеле.
  • Перед запуском необходимо задать и Portfolio, и Security. Инструмент нужен лишь для получения свечей, играющих роль таймера.

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

  1. Запустите стратегию на том портфеле, который требуется защитить, и выберите ликвидный инструмент для тайминга (например, основной фьючерс или индекс).
  2. Настройте EquityPercentFromBalance под желаемый уровень фиксации прибыли.
  3. При росте equity до указанного коэффициента стратегия автоматически закроет все позиции.
  4. После фиксации прибыль переходит в баланс, снимок обновляется, и следующий цикл начнётся заново с новой базой.

Пример

  • Начальный снимок баланса: 10 000 USD.
  • EquityPercentFromBalance = 1.2 → целевое equity = 12 000 USD.
  • Позиции выросли, equity достигло 12 050 USD.
  • Стратегия закрыла все позиции; новый баланс = 12 000 USD.
  • Далее требуется рост до 14 400 USD (12 000 * 1.2), чтобы снова сработало условие фиксации.
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;

/// <summary>
/// Equity percent lock strategy (simplified).
/// Trades using momentum and closes when profit target hit.
/// </summary>
public class EquityPercentLockStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumLength;

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

	public int MomentumLength
	{
		get => _momentumLength.Value;
		set => _momentumLength.Value = value;
	}

	public EquityPercentLockStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_momentumLength = Param(nameof(MomentumLength), 10)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Length", "Momentum period", "Indicators");
	}

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

		var momentum = new Momentum { Length = MomentumLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(momentum, (ICandleMessage candle, decimal momValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (momValue > 0 && Position <= 0)
				{
					BuyMarket();
				}
				else if (momValue < 0 && Position >= 0)
				{
					SellMarket();
				}
			})
			.Start();

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