Ver en GitHub

RSI + MACD Long-Only Strategy

Strategy enters long when RSI crosses above midline with MACD bullish confirmation or when MACD crosses above its signal line while RSI stays above the midline. Exits occur when RSI falls below the midline or MACD crosses below the signal with a non-positive histogram. Optional EMA trend filter and oversold context can refine entries.

Details

  • Entry Criteria: RSI crosses above midline with MACD bullish or MACD crosses above signal with RSI above midline
  • Long/Short: Long only
  • Exit Criteria: RSI crosses below midline or MACD crosses below signal with histogram ≤ 0
  • Stops: Optional percent take profit and stop loss
  • Default Values:
    • RsiLength = 14
    • RsiOversold = 30
    • RsiMidline = 50
    • FastLength = 12
    • SlowLength = 26
    • SignalLength = 9
    • OversoldWindowBars = 10
    • EmaLength = 200
    • TakeProfitPercent = 11.5
    • StopLossPercent = 2.5
  • Filters:
    • Category: Trend
    • Direction: Long
    • Indicators: RSI, MACD, EMA
    • Stops: Yes (optional)
    • Complexity: Intermediate
    • Timeframe: Intraday
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Medium
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>
/// RSI MACD long only strategy using EMA crossover.
/// </summary>
public class RsiMacdLongOnlyStrategy : Strategy
{
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;

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

	public RsiMacdLongOnlyStrategy()
	{
		_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");
	}

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

	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); }
	}
}