Открыть на GitHub

Стратегия Weight Oscillator

Стратегия объединяет RSI, Money Flow Index, Williams %R и DeMarker в взвешенный осциллятор, сглаженный простой скользящей средней. Позиции открываются или разворачиваются при пересечении осциллятором заданных верхнего или нижнего уровней. Режим тренда определяет, следовать ли сигналам осциллятора или торговать против них.

Детали

  • Условия входа:
    • Trend = Direct:
      • Покупка: осциллятор опускается ниже нижнего уровня.
      • Продажа: осциллятор поднимается выше верхнего уровня.
    • Trend = Against:
      • Покупка: осциллятор поднимается выше верхнего уровня.
      • Продажа: осциллятор опускается ниже нижнего уровня.
  • Длинные/короткие: Оба направления.
  • Условия выхода: Обратное пересечение разворачивает позицию.
  • Стопы: Нет явных стопов.
  • Фильтры: Взвешенный осциллятор и сглаживание SMA.
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;
	}
}