Изначальный советник для MetaTrader 5 представлял собой панель управления, с помощью которой трейдер вручную работал максимум с пятью длинными и пятью короткими позициями: удалял имеющиеся тейк-профиты, пересчитывал новые уровни от цены входа и переносил их в безубыток. При портировании на StockSharp визуальный интерфейс исчез, а вся логика защиты позиций стала выполняться автоматически. Стратегия отслеживает совокупную позицию по выбранному инструменту и поддерживает защитный лимитный ордер на тейк-профит, полностью повторяя алгоритм панели.
Автоматизированные действия:
Выставление тейк-профита на расстоянии заданного количества MetaTrader-пунктов от средней цены входа при появлении позиции.
Перенос тейк-профита в точку безубытка, когда рынок проходит требуемое количество пунктов в благоприятную сторону.
Учет брокерских ограничений по freeze/stop уровням из Level1-данных либо расчет защитного зазора через текущий спред и настраиваемый коэффициент.
Отмена защитного ордера при закрытии позиции или отключении управления, что эквивалентно кнопке «Delete TP» в оригинальной панели.
Реализация использует только высокоуровневые возможности StockSharp (SubscribeLevel1, SellLimit, BuyLimit, ReRegisterOrder и т.д.) и автоматически нормализует цену/объем относительно шагов инструмента.
Параметры
Параметр
Назначение
Take profit distance (pips)
Расстояние от цены входа до тейк-профита в MetaTrader-пунктах.
Enable entry-based take profit
Автоматически выставлять тейк-профит от цены входа. При отключении стратегия реагирует только на перенос в безубыток.
Enable break-even
Включить перенос тейк-профита в безубыток при выполнении условия.
Break-even trigger (pips)
Минимальное благоприятное движение (в пунктах), необходимое для переноса в безубыток. Значение 0 означает немедленный перенос.
Manage long positions
Управлять длинной стороной совокупной позиции.
Manage short positions
Управлять короткой стороной позиции.
Remove take profit when disabled
Удалять защитный ордер, если условия управления не выполняются.
Log management actions
Записывать в лог каждое создание/изменение/отмену тейк-профита.
Freeze distance multiplier
Коэффициент для расчета защитного зазора по текущему спреду, когда биржа не публикует ограничения.
Логика работы
При запуске выполняется подписка на Level1-данные, чтобы отслеживать bid/ask и возможные ограничения брокера.
При появлении новых сделок, изменении позиции или обновлении Level1 стратегия заново рассчитывает целевой уровень защиты.
Если позиции нет, существующий тейк-профит отменяется.
При наличии позиции и разрешенной стороне алгоритм:
Вычисляет базовый уровень от цены входа на заданное расстояние (если включена опция).
При активированном безубытке и достаточном движении рынка ограничивает уровень средней ценой входа.
Корректирует цель с учетом freeze/stop расстояний и текущего рынка.
Нормализует цену/объем и регистрирует/перерегистрирует противоположный лимитный ордер.
Если управление стороной отключено, а параметр Remove take profit when disabled активен, ордер отменяется.
Управление рисками
Стратегия не устанавливает стоп-лоссы и не реализует трейлинг или частичное закрытие — только тейк-профит.
Для совместимости с валютными парами автоматически вычисляется стоимость пункта на основе PriceStep и точности инструмента.
При отсутствии данных о брокерских ограничениях параметр коэффициента позволяет создать буфер и избежать отказов в модификации.
Стратегия не инициирует новые входы в рынок; предполагается, что открытия выполняются вручную или сторонними алгоритмами.
Рекомендации по использованию
Подключите стратегию к нужному инструменту и убедитесь, что коннектор предоставляет Level1-данные.
Настройте расстояние тейк-профита, соответствующее значениям из MetaTrader.
Включите перенос в безубыток, если необходимо фиксировать прибыль после движения цены, при значении триггера 0 безубыток активируется сразу.
Отключайте управление для выбранной стороны, если хотите оставить ее под ручным контролем.
При включенном Log management actions отслеживайте сообщения в логе, чтобы убедиться в корректной работе алгоритма.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Manual Position Tracking Panel strategy: MFI crossover.
/// Buys when MFI crosses above 20 (oversold exit), sells when crosses below 80 (overbought exit).
/// </summary>
public class ManualPositionTrackingPanelStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevMfi;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public ManualPositionTrackingPanelStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 14)
.SetGreaterThanZero()
.SetDisplay("Period", "MFI period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMfi = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMfi = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var mfi = new MoneyFlowIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(mfi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal mfiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevMfi < 15 && mfiValue >= 15 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevMfi > 85 && mfiValue <= 85 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevMfi = mfiValue;
_hasPrev = true;
}
}