Открыть на GitHub

Стратегия Easiest Ever Daytrade

Обзор

  • Конвертация эксперта MetaTrader 4 «Easiest ever - daytrade robot» на высокоуровневый API StockSharp.
  • Простая дневная торговля: за сессию открывается не более одной позиции, направление определяется предыдущей дневной свечой.
  • Используются только данные свечей без индикаторов и осцилляторов; все заявки — рыночные.

Логика торговли

  1. Подписка на дневные свечи (DailyCandleType, по умолчанию TimeSpan.FromDays(1)) и сохранение цены открытия и закрытия последнего завершённого дня.
  2. Подписка на внутридневные свечи (IntradayCandleType, по умолчанию TimeSpan.FromMinutes(1)), которые запускают логику входов и выходов.
  3. В утренний период (пока час открытия свечи строго меньше EntryHourLimit, по умолчанию 1):
    • Если дневная свеча закрылась выше открытия, открывается длинная позиция через BuyMarket(TradeVolume).
    • Если дневная свеча закрылась ниже открытия, открывается короткая позиция через SellMarket(TradeVolume).
    • При равенстве открытия и закрытия сделка не совершается.
  4. Позиция удерживается весь день. Когда час свечи становится больше либо равен MarketCloseHour (по умолчанию 20), любая открытая позиция закрывается рыночной заявкой (SellMarket для лонга, BuyMarket для шорта).
  5. Новая сделка возможна только при отсутствии активной позиции, поэтому за день выполняется максимум одна сделка.

Параметры

Параметр Описание Значение по умолчанию
TradeVolume Объём сделки для длинных и коротких входов. Должен быть положительным. 1
EntryHourLimit Последний час (не включая), когда можно открыть новую позицию. Проверяется диапазон [0, 23]. 1
MarketCloseHour Час, в который стратегия принудительно закрывает позицию. Применяется каждый день. 20
IntradayCandleType Таймфрейм для выполнения логики и контроля позиций. TimeSpan.FromMinutes(1).TimeFrame()
DailyCandleType Таймфрейм для чтения открытия и закрытия предыдущего дня. TimeSpan.FromMinutes(5).TimeFrame()

Все параметры созданы через Param() и доступны для оптимизации в среде StockSharp.

Управление рисками

  • Отдельные стоп-лоссы и тейк-профиты не используются; риск ограничивается ежедневным закрытием в MarketCloseHour.
  • В OnStarted вызывается StartProtection() для контроля непредвидённых позиций.
  • Максимальная экспозиция задаётся параметром TradeVolume, так как стратегия допускает лишь одну позицию одновременно.

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

  • Необходимо обеспечить историю как внутридневных, так и дневных свечей. Настройки по умолчанию требуют минутные и дневные данные.
  • Настройте EntryHourLimit и MarketCloseHour под торговую сессию выбранного инструмента.
  • Предполагается, что временные метки свечей соответствуют времени площадки; при отличиях используйте скорректированные источники данных.
  • Логика полностью повторяет оригинального MQL-эксперта, что позволяет воспроизвести его поведение в инфраструктуре StockSharp без Python-версии.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Easiest Ever Daytrade: EMA trend following with ATR stops.
/// </summary>
public class EasiestEverDaytradeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public EasiestEverDaytradeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevClose = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0; _entryPrice = 0;
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (_prevClose == 0 || atrVal <= 0) { _prevClose = close; return; }

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (close > emaVal && _prevClose <= emaVal) { _entryPrice = close; BuyMarket(); }
			else if (close < emaVal && _prevClose >= emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevClose = close;
	}
}