在 GitHub 上查看

WPR Level Cross 策略

该策略基于 Williams %R 指标,当指标突破预设的超买和超卖水平时进行交易。

当指标跌破 Low Level 时,表示可能从超卖区域反转;当指标升破 High Level 时,表示可能从超买区域反转。通过参数 Trend 可以选择顺势交易(Direct)或反向交易(Against)。

参数

  • WprPeriod – Williams %R 的计算周期。
  • HighLevel – 超买阈值。
  • LowLevel – 超卖阈值。
  • Trend – 交易模式:Direct 按指标信号交易,Against 反向交易。
  • EnableBuyEntry / EnableSellEntry – 允许开多/开空。
  • EnableBuyExit / EnableSellExit – 允许平空/平多。
  • StopLoss – 以价格单位表示的止损值。
  • TakeProfit – 以价格单位表示的止盈值。
  • CandleType – 计算所用的K线周期。

工作原理

  1. 策略订阅K线并计算 Williams %R 指标。
  2. 在每根完成的K线上检查指标是否突破设定水平。
  3. 根据 Trend 和允许的操作,使用市价单开仓或平仓。
  4. 通过 StartProtection 启用可选的止损和止盈保护。

说明

  • 代码中的注释为英文。
  • 仅提供 C# 版本,暂不包含 Python 版本。
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>
/// Strategy based on Williams %R indicator crossing specified levels.
/// </summary>
public class WprLevelCrossStrategy : Strategy
{
	private readonly StrategyParam<int> _wprPeriod;
	private readonly StrategyParam<decimal> _highLevel;
	private readonly StrategyParam<decimal> _lowLevel;
	private readonly StrategyParam<TrendModes> _trend;
	private readonly StrategyParam<bool> _enableBuyEntry;
	private readonly StrategyParam<bool> _enableSellEntry;
	private readonly StrategyParam<bool> _enableBuyExit;
	private readonly StrategyParam<bool> _enableSellExit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevWr;

	/// <summary>
	/// Lookback period for Williams %R.
	/// </summary>
	public int WprPeriod
	{
		get => _wprPeriod.Value;
		set => _wprPeriod.Value = value;
	}

	/// <summary>
	/// Upper threshold to detect overbought levels.
	/// </summary>
	public decimal HighLevel
	{
		get => _highLevel.Value;
		set => _highLevel.Value = value;
	}

	/// <summary>
	/// Lower threshold to detect oversold levels.
	/// </summary>
	public decimal LowLevel
	{
		get => _lowLevel.Value;
		set => _lowLevel.Value = value;
	}

	/// <summary>
	/// Trend mode: Direct trades with indicator, Against inverts signals.
	/// </summary>
	public TrendModes Trend
	{
		get => _trend.Value;
		set => _trend.Value = value;
	}

	/// <summary>
	/// Enable opening of long positions.
	/// </summary>
	public bool EnableBuyEntry
	{
		get => _enableBuyEntry.Value;
		set => _enableBuyEntry.Value = value;
	}

	/// <summary>
	/// Enable opening of short positions.
	/// </summary>
	public bool EnableSellEntry
	{
		get => _enableSellEntry.Value;
		set => _enableSellEntry.Value = value;
	}

	/// <summary>
	/// Enable closing of short positions.
	/// </summary>
	public bool EnableBuyExit
	{
		get => _enableBuyExit.Value;
		set => _enableBuyExit.Value = value;
	}

	/// <summary>
	/// Enable closing of long positions.
	/// </summary>
	public bool EnableSellExit
	{
		get => _enableSellExit.Value;
		set => _enableSellExit.Value = value;
	}

	/// <summary>
	/// Stop loss value in price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit value in price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of <see cref="WprLevelCrossStrategy"/>.
	/// </summary>
	public WprLevelCrossStrategy()
	{
		_wprPeriod = Param(nameof(WprPeriod), 14)
			.SetDisplay("WPR Period", "Lookback period for Williams %R", "Indicators")
			
			.SetOptimize(10, 20, 2);

		_highLevel = Param(nameof(HighLevel), -20m)
			.SetDisplay("High Level", "Overbought threshold", "Indicators");

		_lowLevel = Param(nameof(LowLevel), -80m)
			.SetDisplay("Low Level", "Oversold threshold", "Indicators");

		_trend = Param(nameof(Trend), TrendModes.Direct)
			.SetDisplay("Trend Mode", "Direct - trade with indicator; Against - inverse signals", "General");

		_enableBuyEntry = Param(nameof(EnableBuyEntry), true)
			.SetDisplay("Enable Buy Entry", "Allow opening long positions", "Trading");

		_enableSellEntry = Param(nameof(EnableSellEntry), true)
			.SetDisplay("Enable Sell Entry", "Allow opening short positions", "Trading");

		_enableBuyExit = Param(nameof(EnableBuyExit), true)
			.SetDisplay("Enable Buy Exit", "Allow closing short positions", "Trading");

		_enableSellExit = Param(nameof(EnableSellExit), true)
			.SetDisplay("Enable Sell Exit", "Allow closing long positions", "Trading");

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 2000m)
			.SetDisplay("Take Profit", "Take profit in price units", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for candles", "General");
	}

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

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

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

		var wpr = new WilliamsR { Length = WprPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(wpr, ProcessCandle).Start();

		StartProtection(
			new Unit(TakeProfit, UnitTypes.Absolute),
			new Unit(StopLoss, UnitTypes.Absolute));

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

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

		if (_prevWr == 0m)
		{
			_prevWr = wr;
			return;
		}

		var crossedBelowLow = _prevWr > LowLevel && wr <= LowLevel;
		var crossedAboveHigh = _prevWr < HighLevel && wr >= HighLevel;

		if (Trend == TrendModes.Direct)
		{
			if (crossedBelowLow && EnableBuyEntry && Position <= 0)
				BuyMarket();

			if (crossedAboveHigh && EnableSellEntry && Position >= 0)
				SellMarket();
		}
		else
		{
			if (crossedBelowLow && EnableSellEntry && Position >= 0)
				SellMarket();

			if (crossedAboveHigh && EnableBuyEntry && Position <= 0)
				BuyMarket();
		}

		_prevWr = wr;
	}

	/// <summary>
	/// Trend modes for interpreting Williams %R signals.
	/// </summary>
	public enum TrendModes
	{
		/// <summary>
		/// Trade in the direction of indicator signals.
		/// </summary>
		Direct,

		/// <summary>
		/// Invert indicator signals for counter-trend trading.
		/// </summary>
		Against
	}
}