Auf GitHub ansehen

Gewichteter Oszillator-Strategie

Diese Strategie kombiniert RSI, Money Flow Index, Williams %R und DeMarker zu einem gewichteten Oszillator, der durch einen einfachen gleitenden Durchschnitt geglättet wird. Positionen werden geöffnet oder umgekehrt, wenn der Oszillator konfigurierbare hohe oder niedrige Niveaus kreuzt. Der Trendmodus bestimmt, ob Trades den Oszillatorsignalen folgen oder dagegen handeln.

Details

  • Einstiegskriterien:
    • Trend = Direct:
      • Long: Oszillator fällt unter das niedrige Niveau.
      • Short: Oszillator steigt über das hohe Niveau.
    • Trend = Against:
      • Long: Oszillator steigt über das hohe Niveau.
      • Short: Oszillator fällt unter das niedrige Niveau.
  • Long/Short: Beide.
  • Ausstiegskriterien: Gegenüberliegende Kreuzung kehrt die Position um.
  • Stops: Keine expliziten Stops.
  • Filter: Gewichteter Oszillator mit SMA-Glättung.
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>
/// Trading strategy based on a weighted oscillator composed of RSI and Williams %R.
/// Buys when the combined oscillator crosses below the oversold level.
/// Sells when the combined oscillator crosses above the overbought level.
/// </summary>
public class WeightOscillatorStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _wprPeriod;
	private readonly StrategyParam<decimal> _highLevel;
	private readonly StrategyParam<decimal> _lowLevel;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevOsc;
	private bool _hasPrev;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
	public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
	public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public WeightOscillatorStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI length", "Indicators");
		_wprPeriod = Param(nameof(WprPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("WPR Period", "Williams %R length", "Indicators");
		_highLevel = Param(nameof(HighLevel), 70m)
			.SetDisplay("High Level", "Overbought level", "Signals");
		_lowLevel = Param(nameof(LowLevel), 30m)
			.SetDisplay("Low Level", "Oversold level", "Signals");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Working candles", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevOsc = 0m;
		_hasPrev = false;
	}

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var wpr = new WilliamsR { Length = WprPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Normalize WPR from (-100, 0) to (0, 100) and average with RSI
		var normalizedWpr = wprValue + 100m;
		var osc = (rsiValue + normalizedWpr) / 2m;

		if (_hasPrev)
		{
			// Buy when oscillator crosses below low level (oversold)
			if (_prevOsc > LowLevel && osc <= LowLevel && Position <= 0)
			{
				var volume = Volume + Math.Abs(Position);
				BuyMarket(volume);
			}
			// Sell when oscillator crosses above high level (overbought)
			else if (_prevOsc < HighLevel && osc >= HighLevel && Position >= 0)
			{
				var volume = Volume + Math.Abs(Position);
				SellMarket(volume);
			}
		}

		_prevOsc = osc;
		_hasPrev = true;
	}
}