Стратегия переносит основную логику эксперта MetaTrader «DeMarker Pending 2» на высокий уровень StockSharp. Индикатор DeMarker рассчитывается на выбранном таймфрейме, и при пересечении заданных уровней формируются отложенные заявки на покупку или продажу. Заявки можно размещать как стоповыми, так и лимитными с дополнительным отступом от текущей цены. Доступны фильтр торговой сессии, контроль спреда и проверка минимального расстояния до существующих позиций.
Логика торговли
Подписка на нужный поток свечей и вычисление индикатора DeMarker с заданным периодом.
Если предыдущее значение индикатора выше нижнего уровня, а текущее опускается ниже него, формируется очередь на покупку. Если предыдущее значение ниже верхнего уровня, а текущее поднимается выше — готовится продажа. На одной свече обрабатывается только один сигнал.
Отложенные заявки выставляются с учётом точек смещения. При включенной опции старые заявки отменяются до размещения новой. Ограничивается общее число открытых позиций и активных заявок, а также контролируется минимальное расстояние до средней цены позиции.
Для длинных и коротких позиций доступны опциональные стоп-лосс, тейк-профит и трейлинг. Защитные уровни задаются в пунктах и проверяются на каждой закрытой свече. Трейлинг смещается после достижения прибыли активации и дополнительного шага.
Если текущий спред между лучшими котировками превышает порог, новая заявка не ставится. При необходимости можно ограничить торговлю указанным окном по времени.
Параметры
Название
Описание
Working Candles
Таймфрейм для сигналов и контроля позиций.
Order Volume
Объём для отложенных заявок.
Stop Loss (pts)
Начальный стоп-лосс в пунктах.
Take Profit (pts)
Начальный тейк-профит в пунктах.
Trailing Activate (pts)
Прибыль для запуска трейлинга.
Trailing Stop (pts)
Расстояние от цены до трейлинг-стопа.
Trailing Step (pts)
Дополнительная прибыль для переноса трейлинга.
Trail On Close
При включении трейлинг обновляется только на закрытых свечах.
Max Positions
Максимум открытых позиций и активных заявок; 0 отключает ограничение.
Min Distance (pts)
Минимальное расстояние от текущей цены позиции до новой заявки.
Use Stop Orders
Использовать стоп-заявки (true) или лимитные (false).
Single Pending
Разрешать только одну активную отложенную заявку.
Replace Pendings
Перед постановкой новой заявки отменять существующие.
Pending Offset (pts)
Отступ от текущей цены при расчёте заявки.
Max Spread (pts)
Максимальный допустимый спред.
Use Session Filter
Включает фильтр торговой сессии.
Start Hour/Minute, End Hour/Minute
Границы торгового окна при активном фильтре.
DeMarker Period
Период усреднения индикатора DeMarker.
Upper Level
Уровень, формирующий короткие сигналы.
Lower Level
Уровень, формирующий длинные сигналы.
Примечания
Механизмы истечения отложенных заявок и риск-менеджмент по проценту не перенесены — используется фиксированный объём.
Стоп-лосс и тейк-профит контролируются по максимумам/минимумам закрывшейся свечи, что может отличаться от внутрисессионной логики MetaTrader.
Трейлинг работает только после закрытия свечи и не реагирует на тиковые изменения в реальном времени.
Для выставления заявок используются лучшие котировки из Level1. Убедитесь, что подписка на стакан доступна.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Pending order strategy driven by the DeMarker oscillator.
/// Simplified from "DeMarker Pending 2" to use market orders on threshold crossovers.
/// </summary>
public class DeMarkerPending2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _demarkerPeriod;
private readonly StrategyParam<decimal> _demarkerUpperLevel;
private readonly StrategyParam<decimal> _demarkerLowerLevel;
private RelativeStrengthIndex _rsi;
private decimal? _prevOscillator;
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// DeMarker oscillator period.
/// </summary>
public int DemarkerPeriod
{
get => _demarkerPeriod.Value;
set => _demarkerPeriod.Value = value;
}
/// <summary>
/// Upper DeMarker threshold for sell signal.
/// </summary>
public decimal DemarkerUpperLevel
{
get => _demarkerUpperLevel.Value;
set => _demarkerUpperLevel.Value = value;
}
/// <summary>
/// Lower DeMarker threshold for buy signal.
/// </summary>
public decimal DemarkerLowerLevel
{
get => _demarkerLowerLevel.Value;
set => _demarkerLowerLevel.Value = value;
}
public DeMarkerPending2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
.SetDisplay("DeMarker Period", "DeMarker oscillator period", "Indicator")
.SetGreaterThanZero();
_demarkerUpperLevel = Param(nameof(DemarkerUpperLevel), 0.7m)
.SetDisplay("Upper Level", "Overbought threshold", "Indicator");
_demarkerLowerLevel = Param(nameof(DemarkerLowerLevel), 0.3m)
.SetDisplay("Lower Level", "Oversold threshold", "Indicator");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevOscillator = null;
_rsi = new RelativeStrengthIndex { Length = DemarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed)
{
_prevOscillator = rsiValue / 100m;
return;
}
if (_prevOscillator is not decimal prev)
{
_prevOscillator = rsiValue / 100m;
return;
}
var oscillatorValue = rsiValue / 100m;
var volume = Volume;
if (volume <= 0)
volume = 1;
// Cross above lower level from below => buy
if (prev < DemarkerLowerLevel && oscillatorValue >= DemarkerLowerLevel)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Cross below upper level from above => sell
else if (prev > DemarkerUpperLevel && oscillatorValue <= DemarkerUpperLevel)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevOscillator = oscillatorValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_rsi = null;
_prevOscillator = null;
base.OnReseted();
}
}