Auf GitHub ansehen

Intraday-Momentum-Strategie

Handelt innerhalb einer festgelegten Sitzung mit EMA-Kreuzung, RSI-Filter und VWAP-Bestätigung. Geht long, wenn die schnelle EMA über die langsame EMA kreuzt, der RSI unter dem überkauften Niveau liegt und der Kurs über dem VWAP notiert. Shorts bei entgegengesetzten Bedingungen. Wendet feste Stop-Loss- und Take-Profit-Prozentsätze an und schließt alle Positionen am Ende der Sitzung.

Parameter

  • EmaFastLength: Länge der schnellen EMA.
  • EmaSlowLength: Länge der langsamen EMA.
  • RsiLength: RSI-Periode.
  • RsiOverbought: RSI-Überkauft-Niveau.
  • RsiOversold: RSI-Überverkauft-Niveau.
  • StopLossPerc: Stop-Loss-Prozentsatz.
  • TakeProfitPerc: Take-Profit-Prozentsatz.
  • StartHour: Startstunde der Sitzung.
  • StartMinute: Startminute der Sitzung.
  • EndHour: Endstunde der Sitzung.
  • EndMinute: Endminute der Sitzung.
  • CandleType: Kerzentyp.
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>
/// Intraday momentum strategy using EMA crossover, RSI and VWAP.
/// </summary>
public class IntradayMomentumStrategy : Strategy
{
	private readonly StrategyParam<int> _emaFastLength;
	private readonly StrategyParam<int> _emaSlowLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _rsiOverbought;
	private readonly StrategyParam<int> _rsiOversold;
	private readonly StrategyParam<decimal> _stopLossPerc;
	private readonly StrategyParam<decimal> _takeProfitPerc;
	private readonly StrategyParam<int> _startHour;
	private readonly StrategyParam<int> _startMinute;
	private readonly StrategyParam<int> _endHour;
	private readonly StrategyParam<int> _endMinute;
	private readonly StrategyParam<DataType> _candleType;


	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _prevSet;
	private decimal _entryPrice;
	private decimal _stopLoss;
	private decimal _takeProfit;

	/// <summary>
	/// Fast EMA period length.
	/// </summary>
	public int EmaFastLength { get => _emaFastLength.Value; set => _emaFastLength.Value = value; }

	/// <summary>
	/// Slow EMA period length.
	/// </summary>
	public int EmaSlowLength { get => _emaSlowLength.Value; set => _emaSlowLength.Value = value; }

	/// <summary>
	/// RSI period length.
	/// </summary>
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }

	/// <summary>
	/// RSI overbought level.
	/// </summary>
	public int RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }

	/// <summary>
	/// RSI oversold level.
	/// </summary>
	public int RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }

	/// <summary>
	/// Stop-loss percentage.
	/// </summary>
	public decimal StopLossPerc { get => _stopLossPerc.Value; set => _stopLossPerc.Value = value; }

	/// <summary>
	/// Take-profit percentage.
	/// </summary>
	public decimal TakeProfitPerc { get => _takeProfitPerc.Value; set => _takeProfitPerc.Value = value; }

	/// <summary>
	/// Session start hour.
	/// </summary>
	public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }

	/// <summary>
	/// Session start minute.
	/// </summary>
	public int StartMinute { get => _startMinute.Value; set => _startMinute.Value = value; }

	/// <summary>
	/// Session end hour.
	/// </summary>
	public int EndHour { get => _endHour.Value; set => _endHour.Value = value; }

	/// <summary>
	/// Session end minute.
	/// </summary>
	public int EndMinute { get => _endMinute.Value; set => _endMinute.Value = value; }

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public IntradayMomentumStrategy()
	{
		_emaFastLength = Param(nameof(EmaFastLength), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA Length", "Period for fast EMA", "Indicators")
			
			.SetOptimize(5, 20, 1);

		_emaSlowLength = Param(nameof(EmaSlowLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA Length", "Period for slow EMA", "Indicators")
			
			.SetOptimize(10, 50, 1);

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators")
			
			.SetOptimize(7, 28, 1);

		_rsiOverbought = Param(nameof(RsiOverbought), 70)
			.SetRange(0, 100)
			.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
			
			.SetOptimize(60, 90, 5);

		_rsiOversold = Param(nameof(RsiOversold), 30)
			.SetRange(0, 100)
			.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
			
			.SetOptimize(10, 40, 5);

		_stopLossPerc = Param(nameof(StopLossPerc), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
			
			.SetOptimize(0.5m, 5m, 0.5m);

		_takeProfitPerc = Param(nameof(TakeProfitPerc), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
			
			.SetOptimize(1m, 10m, 0.5m);

		_startHour = Param(nameof(StartHour), 0)
			.SetRange(0, 23)
			.SetDisplay("Session Start Hour", "Trading session start hour", "Session");

		_startMinute = Param(nameof(StartMinute), 0)
			.SetRange(0, 59)
			.SetDisplay("Session Start Minute", "Trading session start minute", "Session");

		_endHour = Param(nameof(EndHour), 23)
			.SetRange(0, 23)
			.SetDisplay("Session End Hour", "Trading session end hour", "Session");

		_endMinute = Param(nameof(EndMinute), 59)
			.SetRange(0, 59)
			.SetDisplay("Session End Minute", "Trading session end minute", "Session");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0m;
		_prevSlow = 0m;
		_prevSet = false;
		_entryPrice = 0m;
		_stopLoss = 0m;
		_takeProfit = 0m;
	}

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

		var emaFastInd = new ExponentialMovingAverage { Length = EmaFastLength };
		var emaSlowInd = new ExponentialMovingAverage { Length = EmaSlowLength };
		var rsiInd = new RelativeStrengthIndex { Length = RsiLength };
		var vwapInd = new VolumeWeightedAveragePrice();

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(emaFastInd, emaSlowInd, rsiInd, vwapInd, ProcessCandle)
			.Start();

		StartProtection(null, null);
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaFastVal, IIndicatorValue emaSlowVal, IIndicatorValue rsiVal, IIndicatorValue vwapVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (emaFastVal.IsEmpty || emaSlowVal.IsEmpty || rsiVal.IsEmpty || vwapVal.IsEmpty)
			return;

		var emaFast = emaFastVal.ToDecimal();
		var emaSlow = emaSlowVal.ToDecimal();
		var rsi = rsiVal.ToDecimal();
		var vwap = vwapVal.ToDecimal();

		var time = candle.OpenTime.TimeOfDay;
		var start = new TimeSpan(StartHour, StartMinute, 0);
		var end = new TimeSpan(EndHour, EndMinute, 0);
		var inSession = time >= start && time <= end;

		if (!inSession)
		{
			if (Position > 0)
				SellMarket();
			else if (Position < 0)
				BuyMarket();
			return;
		}

		if (!_prevSet)
		{
			_prevFast = emaFast;
			_prevSlow = emaSlow;
			_prevSet = true;
			return;
		}

		var crossover = _prevFast <= _prevSlow && emaFast > emaSlow;
		var crossunder = _prevFast >= _prevSlow && emaFast < emaSlow;

		_prevFast = emaFast;
		_prevSlow = emaSlow;

		var longCondition = crossover && rsi < RsiOverbought && candle.ClosePrice > vwap;
		var shortCondition = crossunder && rsi > RsiOversold && candle.ClosePrice < vwap;

		if (longCondition && Position <= 0)
		{
			_entryPrice = candle.ClosePrice;
			_stopLoss = _entryPrice * (1 - StopLossPerc / 100m);
			_takeProfit = _entryPrice * (1 + TakeProfitPerc / 100m);
			BuyMarket();
		}
		else if (shortCondition && Position >= 0)
		{
			_entryPrice = candle.ClosePrice;
			_stopLoss = _entryPrice * (1 + StopLossPerc / 100m);
			_takeProfit = _entryPrice * (1 - TakeProfitPerc / 100m);
			SellMarket();
		}

		if (Position > 0)
		{
			if (candle.LowPrice <= _stopLoss || candle.HighPrice >= _takeProfit)
				SellMarket();
		}
		else if (Position < 0)
		{
			if (candle.HighPrice >= _stopLoss || candle.LowPrice <= _takeProfit)
				BuyMarket();
		}
	}
}