GitHub で見る

Premarket Gap MomoTrader Strategy

This strategy trades a single long breakout during the premarket session when the current candle gains at least a specified percentage above the previous close, prints a bullish candle with sufficient volume, and the candle body occupies a large part of its range. Position size is scaled depending on the body size.

After entry the strategy holds the position while the next candles remain bullish and their volume increases. A red candle or non-increasing volume exits the position. Only one trade is allowed per day and trading can be restricted to the 04:00–09:30 session.

Details

  • Entry Criteria:
    • Current candle gain ≥ MinGainPct compared to previous close.
    • Candle is green and Volume > MinVolume.
    • Body percent defines position size: ≥90% → 100%, ≥85% → 50%, ≥75% → 25%.
    • Optional session filter 04:00–09:30 if UseSession is enabled.
  • Exit Criteria:
    • First red candle or candle with non-increasing volume after entry.
  • Stops: No.
  • Default Values:
    • MinGainPct = 5.
    • MinVolume = 15000.
    • UseSession = true.
  • Filters:
    • Category: Momentum
    • Direction: Long
    • Indicators: None
    • Stops: No
    • Complexity: Medium
    • Timeframe: Intraday
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Premarket Gap MomoTrader strategy using EMA crossover.
/// </summary>
public class PremarketGapMomoTraderStrategy : Strategy
{
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance.
	/// </summary>
	public PremarketGapMomoTraderStrategy()
	{
		_slowLength = Param(nameof(SlowLength), 40)
			.SetGreaterThanZero()
			.SetDisplay("Slow Length", "Slow EMA period", "General");

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

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

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

		var fast = new ExponentialMovingAverage { Length = 14 };
		var slow = new ExponentialMovingAverage { Length = SlowLength };

		var prevF = 0m;
		var prevS = 0m;
		var init = false;
		var lastSignal = DateTimeOffset.MinValue;
		var cooldown = TimeSpan.FromMinutes(360);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, (candle, f, s) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!fast.IsFormed || !slow.IsFormed)
					return;

				if (!init)
				{
					prevF = f;
					prevS = s;
					init = true;
					return;
				}

				if (candle.OpenTime - lastSignal >= cooldown)
				{
					if (prevF <= prevS && f > s && Position <= 0)
					{
						BuyMarket();
						lastSignal = candle.OpenTime;
					}
					else if (prevF >= prevS && f < s && Position >= 0)
					{
						SellMarket();
						lastSignal = candle.OpenTime;
					}
				}

				prevF = f;
				prevS = s;
			})
			.Start();

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