Открыть на GitHub

Стратегия Genie Pivot Fixed

Эта стратегия реализует скальперскую систему разворота "Genie", изначально написанную на MQL4. Она анализирует последние восемь свечей, чтобы обнаруживать резкие развороты на ключевых уровнях. Длинная позиция открывается, когда семь подряд минимумов понижаются и текущая свеча формирует более высокий минимум и закрывается выше предыдущего максимума. Короткая позиция открывается, когда семь подряд максимумов повышаются и текущая свеча формирует более низкий максимум и закрывается ниже предыдущего минимума.

Стратегия использует фиксированный размер позиции (Strategy.Volume) и применяет как трейлинг-стоп, так и тейк-профит, измеряемые в абсолютных единицах цены. Эти параметры можно оптимизировать, что позволяет захватывать быстрые развороты и защищать накопленную прибыль.

Детали

  • Условия входа:
    • Лонг: Low[7] > Low[6] > ... > Low[1] и Low[1] < Low[0] и High[1] < Close[0].
    • Шорт: High[7] < High[6] < ... < High[1] и High[1] > High[0] и Low[1] > Close[0].
  • Лонг/Шорт: обе стороны.
  • Условия выхода:
    • Срабатывание трейлинг-стопа или тейк-профита.
  • Стопы:
    • Тейк-профит: абсолютное расстояние от точки входа.
    • Трейлинг-стоп: абсолютное расстояние, подтягивающееся по мере движения сделки.
  • Значения по умолчанию:
    • TakeProfit = 500.
    • TrailingStop = 200.
    • CandleType = 1 минута.
  • Фильтры:
    • Категория: Разворот.
    • Направление: Обе стороны.
    • Индикаторы: Нет.
    • Стопы: Да.
    • Сложность: Простая.
    • Таймфрейм: Краткосрочный.
    • Сезонность: Нет.
    • Нейросети: Нет.
    • Дивергенция: Нет.
    • Уровень риска: Средний.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Genie pivot reversal strategy.
/// Enters long after a sequence of falling lows followed by an upside breakout.
/// Enters short after a sequence of rising highs followed by a downside breakout.
/// Applies trailing stop and take-profit in absolute price units.
/// </summary>
public class GeniePivotFixedStrategy : Strategy
{
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _trailingStop;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private readonly decimal[] _lows = new decimal[8];
	private readonly decimal[] _highs = new decimal[8];
	private int _stored;
	private int _cooldownRemaining;

	/// <summary>
	/// Take-profit distance in price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in price units.
	/// </summary>
	public decimal TrailingStop
	{
		get => _trailingStop.Value;
		set => _trailingStop.Value = value;
	}

	/// <summary>
	/// Candle type used for analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Number of completed candles to wait after a position change.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public GeniePivotFixedStrategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Target profit in price units", "Risk Management")
			
			.SetOptimize(100m, 1000m, 100m);

		_trailingStop = Param(nameof(TrailingStop), 200m)
			.SetGreaterThanZero()
			.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management")
			
			.SetOptimize(50m, 500m, 50m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for candles", "General");

		_cooldownBars = Param(nameof(CooldownBars), 4)
			.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_stored = 0;
		_cooldownRemaining = 0;
		Array.Clear(_lows);
		Array.Clear(_highs);
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

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

		StartProtection(
			takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
			stopLoss: new Unit(TrailingStop, UnitTypes.Absolute),
			isStopTrailing: true);

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

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		for (var i = 7; i > 0; i--)
		{
			_lows[i] = _lows[i - 1];
			_highs[i] = _highs[i - 1];
		}

		_lows[0] = candle.LowPrice;
		_highs[0] = candle.HighPrice;
		if (_stored < 8)
			_stored++;

		if (_stored < 5)
			return;

		if (_cooldownRemaining > 0)
			return;

		var buySeq = _lows[4] > _lows[3] && _lows[3] > _lows[2] && _lows[2] > _lows[1];

		if (buySeq && _lows[1] < _lows[0] && _highs[1] < candle.ClosePrice && candle.ClosePrice > candle.OpenPrice)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
			_cooldownRemaining = CooldownBars;
			return;
		}

		var sellSeq = _highs[4] < _highs[3] && _highs[3] < _highs[2] && _highs[2] < _highs[1];

		if (sellSeq && _highs[1] > _highs[0] && _lows[1] > candle.ClosePrice && candle.ClosePrice < candle.OpenPrice)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
			_cooldownRemaining = CooldownBars;
		}
	}
}