Открыть на GitHub

Стратегия Aeron JJN Scalper EA

Обзор

Стратегия представляет собой порт экспертного советника Aeron JJN Scalper на высокоуровневый API StockSharp. Алгоритм анализирует только завершённые свечи, ищет двухсвечные разворотные комбинации и запоминает цену открытия последней сильной свечи в противоположную сторону. Этот уровень рассматривается как отложенный стоп-ордер: при его достижении отправляется рыночная заявка, устанавливаются ATR-ориентиры по стоп-лоссу и тейк-профиту, а дальнейшее сопровождение ведётся с помощью трейлинг-стопа в пунктах.

Ключевые идеи:

  • Направление сделки определяется двухсвечным разворотом (бычья/медвежья комбинация).
  • Точка входа — открытие последней «сильной» свечи противоположного цвета.
  • Значение ATR(8), рассчитанное на свечке сигнала, задаёт расстояние до стоп-лосса и тейк-профита.
  • Трейлинг-стоп срабатывает, когда цена проходит заданное количество пунктов в прибыльную сторону.
  • Отложенные уровни автоматически отменяются по истечении заданного времени.

Правила торговли

Поиск сигнала

  1. Используются только закрытые свечи выбранного таймфрейма (по умолчанию 1 минута).
  2. Размер пункта вычисляется из шага цены инструмента; для трёх- и пятизнаковых котировок дополнительно применяется коэффициент 10, как в MetaTrader.
  3. В памяти хранится до 120 последних свечей для поиска подходящих исторических баров.
  4. Сигнал на покупку появляется, если:
    • текущая свеча закрылась выше открытия;
    • предыдущая свеча — медвежья, и её тело больше DojiDiff1Pips;
    • при поиске назад находится ближайшая медвежья свеча с телом больше DojiDiff2Pips, её открытие — уровень стоп-покупки.
  5. Сигнал на продажу формируется зеркально: текущая свеча медвежья, предыдущая — сильная бычья, уровень берётся по открытию последней бычьей свечи с телом больше DojiDiff2Pips.
  6. Новые сигналы игнорируются, если уже есть уровень в ту же сторону или ATR ещё не посчитан.

Управление отложенными уровнями

  • Сохранённый уровень рассматривается как отложенный ордер и удаляется, если цена не достигает его до истечения ResetMinutes минут.
  • Если в последующих свечах максимум ≥ уровня покупки (или минимум ≤ уровня продажи), стратегия отправляет рыночную заявку нужного объёма: позиция переворачивается и добавляется Volume лотов.
  • После открытия позиции противоположный уровень сбрасывается.

Стоп-лосс, тейк-профит и трейлинг

  • При входе фиксируется значение ATR(8) свечи сигнала:
    • Для лонга: стоп-лосс = entry - ATR, тейк-профит = entry + ATR.
    • Для шорта: стоп-лосс = entry + ATR, тейк-профит = entry - ATR.
  • На каждой завершённой свече выполняются проверки:
    • если цена достигла стоп-уровня или цели, позиция закрывается рыночной заявкой;
    • когда прибыль превышает TrailingStopPips + TrailingStepPips пунктов, трейлинг-стоп переносится на расстояние TrailingStopPips от текущего закрытия и никогда не отступает назад.
  • При ручном закрытии позиции внутренние переменные очищаются автоматически.

Параметры

Параметр Значение по умолчанию Описание
Volume 0.1 Размер чистой позиции при входе; при развороте добавляется абсолютная величина текущей позиции.
TrailingStopPips 5 Базовое расстояние трейлинг-стопа в пунктах (переводится в цену).
TrailingStepPips 5 Дополнительное движение в пунктах до очередного переноса трейлинга.
ResetMinutes 10 Время жизни отложенного уровня (минуты).
DojiDiff1Pips 10 Минимальное тело предыдущей свечи для подтверждения разворота.
DojiDiff2Pips 4 Минимальное тело свечи, используемой как уровень входа.
CandleType 1 минута Тип свечей для расчётов.

Особенности реализации

  • Стратегия работает на уровне завершённых свечей и не выставляет реальные стоп-заявки: при пробое уровня выполняется рыночная сделка, что повторяет логику исходного советника в рамках высокоуровневого API.
  • ATR(8) рассчитывается индикатором AverageTrueRange, чтобы стопы и цели оставались постоянными для каждой сделки.
  • Преобразование пунктов повторяет метатрейдеровский подход к трёх- и пятизнаковым котировкам; при отсутствии PriceStep используется значение 1.
  • В памяти поддерживается до 120 свечей, что немного превышает исходный вызов CopyRates на 100 баров.
  • Python-версия стратегии не создавалась.

Использование

  1. Подключите стратегию к нужному инструменту и портфелю.
  2. Настройте таймфрейм свечей, параметры точек и ATR под особенности инструмента.
  3. Запустите стратегию — она будет отслеживать сигналы, входить при достижении уровней и сопровождать позиции автоматически.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the Aeron JJN Scalper expert advisor.
/// Detects reversal candle patterns (bullish candle after strong bearish, and vice versa)
/// and enters at breakout of the prior candle's open level.
/// </summary>
public class AeronJjnScalperEaStrategy : Strategy
{
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _bodyMinAtr;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<DataType> _candleType;

	private AverageTrueRange _atr;
	private decimal _prevOpen;
	private decimal _prevClose;
	private bool _hasPrev;
	private int _cooldown;

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal BodyMinAtr
	{
		get => _bodyMinAtr.Value;
		set => _bodyMinAtr.Value = value;
	}

	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

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

	public AeronJjnScalperEaStrategy()
	{
		_atrLength = Param(nameof(AtrLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Length", "ATR indicator period", "Indicators");

		_bodyMinAtr = Param(nameof(BodyMinAtr), 1.5m)
			.SetDisplay("Body Min ATR", "Minimum candle body size as ATR multiple", "Indicators");

		_cooldownBars = Param(nameof(CooldownBars), 10)
			.SetGreaterThanZero()
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle Type", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_hasPrev = false;
		_cooldown = 0;
		_prevOpen = 0;
		_prevClose = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_atr = new AverageTrueRange { Length = AtrLength };
		_hasPrev = false;
		_cooldown = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(_atr, ProcessCandle).Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (atrVal <= 0)
		{
			SavePrev(candle);
			return;
		}

		if (_cooldown > 0)
			_cooldown--;

		if (_hasPrev && _cooldown == 0 && Position == 0)
		{
			var prevBody = Math.Abs(_prevClose - _prevOpen);
			var minBody = atrVal * BodyMinAtr;

			// Bullish candle after strong bearish candle => buy
			if (candle.ClosePrice > candle.OpenPrice && _prevClose < _prevOpen && prevBody >= minBody)
			{
				BuyMarket();
				_cooldown = CooldownBars;
			}
			// Bearish candle after strong bullish candle => sell
			else if (candle.ClosePrice < candle.OpenPrice && _prevClose > _prevOpen && prevBody >= minBody)
			{
				SellMarket();
				_cooldown = CooldownBars;
			}
		}

		SavePrev(candle);
	}

	private void SavePrev(ICandleMessage candle)
	{
		_prevOpen = candle.OpenPrice;
		_prevClose = candle.ClosePrice;
		_hasPrev = true;
	}
}