Открыть на GitHub

Стратегия EA Close

Обзор

EA Close Strategy — это прямая портировка оригинального советника MQL5 «EA Close», созданного Владимиром Карпутовым, в инфраструктуру StockSharp. Стратегия объединяет Commodity Channel Index (CCI), взвешенную скользящую среднюю (WMA) и стохастический осциллятор, чтобы фиксировать истощение импульса в конце откатов. Расчёты выполняются только на завершённых свечах, что повторяет логику «нового бара» из исходного советника.

Реализация StockSharp сохраняет набор параметров и структуру версии MQL, поэтому ранее найденные оптимальные настройки можно использовать повторно. Сигналы строятся по данным предыдущей завершённой свечи, что делает поведение стратегии детерминированным при прогоне на истории.

Индикаторы

  • Commodity Channel Index (CCI) — определяет экстремумы перекупленности и перепроданности относительно средней цены за заданный период.
  • Weighted Moving Average (WMA) — мини-фильтр тренда; в оригинальном советнике используется LWMA с периодом 1 по взвешенной цене, что фактически даёт сглаженное значение цены свечи. В порте WMA применяется к потоку свечей напрямую.
  • Стохастический осциллятор (%K) — подтверждает истощение импульса по классическим уровням перекупленности и перепроданности.

Торговая логика

  1. Условия для покупки
    • CCI предыдущей свечи опускается ниже -CciLevel.
    • Значение стохастика %K на предыдущей свече ниже StochasticLevelDown.
    • Цена открытия предыдущей свечи выше значения WMA для этой свечи.
    • Если все условия выполнены и текущая позиция не положительная, стратегия покупает. Открытые короткие позиции закрываются тем же рыночным ордером.
  2. Условия для продажи
    • CCI предыдущей свечи поднимается выше CciLevel.
    • Стохастик %K на предыдущей свече выше StochasticLevelUp.
    • Цена закрытия предыдущей свечи ниже значения WMA для этой свечи.
    • При выполнении условий и отсутствии отрицательной позиции стратегия продаёт. Открытые длинные позиции закрываются тем же ордером.

Используются только финальные данные свечей. Это повторяет фильтр OnTick в исходном коде и исключает перерисовку внутри бара.

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

В методе OnStarted вызывается StartProtection, что воспроизводит фиксированные дистанции стоп-лосса и тейк-профита из версии MQL. Значения задаются в пунктах. Вспомогательная функция переводит пункты в цену, умножая шаг цены инструмента на 10, если точность шага содержит три или пять знаков после запятой (например, 0.001 или 0.00001), как и в оригинальном советнике для трёх- и пятизнаковых котировок. Нулевое значение отключает соответствующий защитный ордер.

Параметры

Имя Описание Значение по умолчанию
Volume Объём рыночных заявок. 1
StopLossPips Расстояние стоп-лосса в пунктах. 35
TakeProfitPips Расстояние тейк-профита в пунктах. 75
CciPeriod Период усреднения индикатора CCI. 14
CciLevel Абсолютный порог для определения экстремумов CCI. 120
MaPeriod Период фильтра WMA. 1
StochasticLength Окно расчёта стохастика (диапазон максимумов/минимумов). 5
StochasticKPeriod Сглаживание линии %K. 3
StochasticDPeriod Сглаживание линии %D. 3
StochasticLevelUp Уровень перекупленности для линии %K. 70
StochasticLevelDown Уровень перепроданности для линии %K. 30
CandleType Тип свечей, используемых в расчётах. Таймфрейм 1 час

Особенности использования

  • Стратегия сохраняет значения индикаторов и цен предыдущей завершённой свечи и анализирует сигналы на открытии следующего бара, повторяя вызов CopyBuffer(..., start=1) из MQL.
  • Рыночные заявки рассчитываются таким образом, чтобы закрыть встречную позицию и одновременно открыть новую — аналогично функции ClosePositions в исходном коде.
  • В StockSharp у StochasticOscillator параметр Length задаёт окно расчёта, KPeriod — сглаживание %K, DPeriod — сглаживание %D, что соответствует параметрам iStochastic (K-period, slowing, D-period).
  • Поскольку StockSharp работает с агрегированными свечами, отдельное обновление котировок не требуется — подписка сама передаёт индикаторам завершённые свечи.

Примечания по конвертации

  • Python-версия намеренно не создавалась в рамках задачи.
  • Взвешенная средняя применяется к свечам; при необходимости воспроизвести точную формулу MT5 (High + Low + 2 * Close) / 4 можно предварительно преобразовать данные перед подачей в WMA.
  • Защитные ордера управляются платформой через StartProtection, поэтому дополнительная регистрация стопов и тейков после каждой сделки не требуется.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class EaCloseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private decimal? _prevCci;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }

	public EaCloseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 14).SetGreaterThanZero().SetDisplay("CCI Period", "CCI lookback", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCci = null;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal cciVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevCci = cciVal; return; }
		if (_prevCci == null) { _prevCci = cciVal; return; }
		if (_prevCci.Value < 0m && cciVal >= 0m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevCci.Value > 0m && cciVal <= 0m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevCci = cciVal;
	}
}