Close Orders Strategy — утилита для управления рисками, повторяющая логику MQL-советника CloseOrders.mq4. Стратегия непрерывно отслеживает плавающий финансовый результат открытых позиций и автоматически закрывает подходящие ордера при достижении целевой прибыли или заданного уровня убытка. Такой подход помогает защищать счёт и синхронизировать выходы в комплексных торговых системах.
Принцип работы
Стратегия подписывается на настраиваемую серию свечей (по умолчанию 1 минута) и пересчитывает плавающий PnL после закрытия каждой свечи.
Плавающий результат вычисляется по позициям портфеля. Если указан магический номер, учитываются только позиции с StrategyId, совпадающим с выбранным значением.
Когда плавающая прибыль достигает или превышает целевое значение, все соответствующие ордера и позиции закрываются.
Если плавающий результат опускается до заданного уровня убытка (отрицательное число), запускается процедура принудительного закрытия для ограничения потерь.
Перед отправкой рыночных заявок стратегия отменяет активные ордера, удовлетворяющие фильтру магического номера, чтобы во время ликвидации не открывались новые позиции.
Процедура ликвидации повторяется до полного закрытия всех подходящих позиций, что позволяет корректно обработать частичные исполнения.
Параметры
Параметр
Описание
Target Profit Money
Минимальная величина плавающей прибыли (в валюте счёта), при которой инициируется закрытие. Значение должно быть больше нуля.
Cut Loss Money
Допустимый уровень плавающего убытка (в валюте счёта). Значение 0 отключает контроль по убытку.
Magic Number
Необязательный идентификатор стратегии. Если оставить поле пустым, будут обрабатываться все позиции; при заполнении закрываются только позиции с соответствующим StrategyId.
Candle Type
Тип свечей, используемый для периодической проверки PnL. При необходимости можно выбрать более короткий таймфрейм для ускоренной реакции.
Особенности реализации
Магический номер из MQL сопоставляется со значениями UserOrderId/StrategyId в StockSharp. В управляемых стратегиях следует использовать одинаковый идентификатор.
Код оформлен с использованием табуляции и соответствует общему шаблону конвертируемых стратегий.
Перед ликвидацией позиции отменяются соответствующие отложенные ордера, что исключает повторный вход в рынок.
При интеграции с живыми торговыми роботами можно дополнительно активировать защитные механизмы (например, StartProtection()).
Рекомендации по применению
Запускайте стратегию вместе с торговыми алгоритмами, которые проставляют собственный StrategyId, чтобы централизованно управлять выходом из позиций.
Корректируйте параметр Candle Type, подбирая баланс между скоростью реакции и нагрузкой на систему.
Подключите уведомления, чтобы оперативно узнавать о срабатывании автоматического закрытия позиций.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Close Orders Risk Control strategy: CCI crossover with risk management.
/// Buys when CCI crosses above zero, sells when crosses below zero.
/// </summary>
public class CloseOrdersRiskControlStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLevel;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevCci;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public CloseOrdersRiskControlStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
_cciLevel = Param(nameof(CciLevel), 100m)
.SetDisplay("CCI Level", "CCI threshold for crossover", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevCci < -CciLevel && cciValue >= -CciLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevCci > CciLevel && cciValue <= CciLevel && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevCci = cciValue;
_hasPrev = true;
}
}