Открыть на GitHub

Стратегия «Ордера в заданный день и время»

Стратегия повторяет логику советника MetaTrader «Expert Advisor specific day and time».
В заданный момент времени она размещает рыночные или отложенные ордера на покупку/продажу и может закрыть все позиции в другой момент.
Версия на StockSharp сохраняет исходное управление рисками: тейк-профит, стоп-лосс, трейлинг и перевод в безубыток.

Основная логика

  1. Расписание

    • OpenTime — время, когда выставляются ордера.
    • CloseTime — время, когда позиции закрываются и удаляются отложенные ордера.
      Проверка выполняется в минутном окне, что соответствует сравнению TimeToString(..., TIME_MINUTES) в MQL.
  2. Тип ордеров

    • OrderPlacement переключает режим между рынком, стоп-ордером и лимитным ордером.
    • Флаги OpenBuyOrders и OpenSellOrders задают направление.
    • OrderDistancePoints определяет сдвиг цены для отложенных ордеров в пунктах.
    • PendingExpireMinutes автоматически отменяет неисполненные ордера по истечении срока.
  3. Расчёт объёма

    • LotSizing = Manual использует фиксированный ManualVolume.
    • LotSizing = Automatic рассчитывает объём по формуле volume = (portfolio / contractSize) * RiskFactor.
      Итог округляется по Security.VolumeStep и ограничивается MinVolume/MaxVolume, если биржа задаёт такие границы.
  4. Защита позиции

    • StopLossPoints и TakeProfitPoints конвертируются в абсолютные цены через размер пункта инструмента.
    • Параметры TrailingStopEnabled, TrailingStepPoints и BreakEvenEnabled перемещают стоп точно так же, как в оригинальном советнике, используя поток bid/ask.
    • При достижении уровней стопа или тейка позиция закрывается рыночным ордером, что соответствует постоянному переносу стоп-ордера в MQL.
  5. Фаза закрытия

    • При включённых CloseOwnOrders или CloseAllOrders стратегия закрывает позицию в окне закрытия.
    • DeletePendingOrders удаляет все оставшиеся отложенные ордера.

Параметры

Параметр Описание
OpenTime, CloseTime Моменты открытия и закрытия (UTC).
OrderPlacement Тип выставляемых ордеров: рынок, стоп или лимит.
OpenBuyOrders, OpenSellOrders Выбор направления торговли.
TakeProfitPoints, StopLossPoints Дистанции тейка и стопа в пунктах (0 отключает).
TrailingStopEnabled, TrailingStepPoints Включают трейлинг и задают минимальный шаг продвижения.
BreakEvenEnabled, BreakEvenAfterPoints Переводят стоп в безубыток после достижения указанной прибыли.
OrderDistancePoints Сдвиг цены для отложенных ордеров.
PendingExpireMinutes Срок действия отложенных ордеров в минутах.
LotSizing Режим расчёта объёма — ручной или автоматический.
RiskFactor, ManualVolume Параметры для выбранного режима расчёта объёма.
CloseOwnOrders, CloseAllOrders, DeletePendingOrders Управляют закрытием позиций и удалением ордеров в конце.

Дополнительно

  • Класс находится в пространстве имён StockSharp.Samples.Strategies и использует табуляцию для отступов, как требует репозиторий.
  • Стратегия подписывается на Level1, чтобы получать лучшие цены и корректно воспроизводить условия срабатывания трейлинга и отложенных ордеров.
  • Поле MagicNumber из MT4 не требуется — StockSharp отделяет ордера каждой стратегии автоматически.

Как использовать

  1. Соберите решение AlgoTrading.sln и прикрепите стратегию к нужному инструменту и портфелю.
  2. Настройте время открытия/закрытия, тип ордеров и параметры риска.
  3. Запустите стратегию до наступления OpenTime — она сама выставит ордера в нужный момент.
  4. Оставьте стратегию активной до окончания окна CloseTime, если нужно автоматическое закрытие.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Specific Day Time strategy: SMA crossover with time filter.
/// Trades only during specific hours, using fast/slow SMA crossover.
/// </summary>
public class SpecificDayTimeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _tradeHour;
	private readonly StrategyParam<int> _cooldownBars;

	private int _cooldown;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int TradeHour { get => _tradeHour.Value; set => _tradeHour.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public SpecificDayTimeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 8)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
		_tradeHour = Param(nameof(TradeHour), 12)
			.SetDisplay("Trade Hour", "Hour when entries are allowed", "Trading")
			.SetRange(0, 23);
		_cooldownBars = Param(nameof(CooldownBars), 8)
			.SetGreaterThanZero()
			.SetDisplay("Cooldown Bars", "Bars to wait between orders", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_cooldown = 0;
		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_cooldown > 0)
			_cooldown--;

		var bullishTrend = fastValue > slowValue;
		var bearishTrend = fastValue < slowValue;

		if (_cooldown == 0 && candle.OpenTime.Hour == TradeHour)
		{
			if (bullishTrend && Position <= 0)
			{
				BuyMarket();
				_cooldown = CooldownBars;
			}
			else if (bearishTrend && Position >= 0)
			{
				SellMarket();
				_cooldown = CooldownBars;
			}
		}
		else if (_cooldown == 0)
		{
			if (Position > 0 && bearishTrend)
			{
				SellMarket();
				_cooldown = CooldownBars;
			}
			else if (Position < 0 && bullishTrend)
			{
				BuyMarket();
				_cooldown = CooldownBars;
			}
		}
	}
}