GitHub で見る

Genie Pivot Fixed Strategy

This strategy implements the "Genie" pivot point reversal scalping system originally written in MQL4. It scans the last eight candles to detect sudden reversals at pivot points. A long trade is triggered when seven consecutive lows decrease and the current candle makes a higher low while closing above the previous high. A short trade is triggered when seven consecutive highs increase and the current candle makes a lower high while closing below the previous low.

The strategy uses a fixed position size (Strategy.Volume) and applies both a trailing stop and a take-profit measured in absolute price units. These parameters can be optimized and allow the method to capture quick reversals while protecting open profits.

Details

  • Entry Criteria:
    • Long: Low[7] > Low[6] > ... > Low[1] && Low[1] < Low[0] && High[1] < Close[0].
    • Short: High[7] < High[6] < ... < High[1] && High[1] > High[0] && Low[1] > Close[0].
  • Long/Short: Both.
  • Exit Criteria:
    • Trailing stop or take-profit is hit.
  • Stops:
    • Take-profit: absolute distance from entry.
    • Trailing stop: absolute distance, trailing as the trade moves in favor.
  • Default Values:
    • TakeProfit = 500.
    • TrailingStop = 200.
    • CandleType = 1 minute.
  • Filters:
    • Category: Reversal.
    • Direction: Both.
    • Indicators: None.
    • Stops: Yes.
    • Complexity: Simple.
    • Timeframe: Short-term.
    • Seasonality: No.
    • Neural networks: No.
    • Divergence: No.
    • Risk level: Medium.
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;
		}
	}
}