在 GitHub 上查看

Vlado 策略

基于 Larry Williams %R 指标的动量反转策略。系统等待指标进入深度超卖或超买区,然后在下一根收盘蜡烛上完成持仓翻转。此 StockSharp 版本完整保留 MetaTrader 脚本的核心思路,并把关键参数全部开放给使用者调整。

概览

  • 类型:振荡器驱动的逆势策略。
  • 适用市场:提供稳定蜡烛数据的高流动性品种,例如外汇主流货币对、指数期货、加密货币现货。
  • 时间框架:通过 CandleType 参数配置,默认使用 1 小时蜡烛,与原脚本一致。
  • 持仓方向:支持多头与空头,只会在任意时刻保持一个仓位,出现反向信号时自动翻仓。
  • 核心指标:Williams %R,可自定义计算周期及阈值。

工作原理

  1. 订阅指定的蜡烛序列,并在每根收盘蜡烛上计算 Williams %R。
  2. 默认超卖阈值为 -75,超买阈值为 -25(该指标数值区间位于 -100 到 0 之间)。
  3. 当 %R <= OversoldLevel 时,策略在下一根收盘后建立或翻转为多头仓位。
  4. 当 %R >= OverboughtLevel 时,策略建立或翻转为空头仓位。
  5. 下单数量为 Volume + Math.Abs(Position),意味着翻仓时会用单笔市价单完成平旧仓与开新仓。
  6. 策略未内置止损止盈,风险控制依赖阈值、时间框架及外部资金管理。
  7. 所有信号均通过 LogInfo 输出,方便在 StockSharp 前端或日志文件中复盘。

参数说明

  • WilliamsPeriod:指标计算周期,数值越大越平滑,越小越敏感。
  • OverboughtLevel:判定超买的阈值(默认 -25),支持优化。
  • OversoldLevel:判定超卖的阈值(默认 -75),支持优化。
  • CandleType:用于计算的蜡烛类型与时间框架,可选择时间、成交量或区间蜡烛。
  • Volume(继承自 Strategy):基础下单手数,需根据账户规模与风险承受力自行设定。

交易规则

  • 做多条件:%R <= OversoldLevel 且当前仓位为空或做空。
  • 做空条件:%R >= OverboughtLevel 且当前仓位为空或做多。
  • 离场方式:出现反向信号时直接翻仓,相当于同时完成出场与进场。
  • 仓位管理:策略不做加仓或分批出场,始终保持单一仓位。

额外提示

  • 更适合区间或震荡市况,若市场快速单边运行需要配合其他过滤器使用。
  • 建议在实盘中额外叠加资金止损、交易时段限制等风险管理模块。
  • 实现包含图表输出:主图绘制蜡烛与成交,副图展示 Williams %R 曲线。
  • 所有关键参数均已允许在 StockSharp 优化器中进行批量测试。
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>
/// Williams %R reversal strategy.
/// </summary>
public class VladoStrategy : Strategy
{
	private readonly StrategyParam<int> _williamsPeriod;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<DataType> _candleType;

	private WilliamsR _williamsR;

	public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
	public decimal OverboughtLevel { get => _overboughtLevel.Value; set => _overboughtLevel.Value = value; }
	public decimal OversoldLevel { get => _oversoldLevel.Value; set => _oversoldLevel.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public VladoStrategy()
	{
		_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Williams Period", "Williams %R lookback.", "Indicator");
		_overboughtLevel = Param(nameof(OverboughtLevel), -25m)
			.SetDisplay("Overbought Level", "Williams %R short threshold.", "Signal");
		_oversoldLevel = Param(nameof(OversoldLevel), -75m)
			.SetDisplay("Oversold Level", "Williams %R long threshold.", "Signal");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe.", "General");
	}

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

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

		_williamsR = new WilliamsR { Length = WilliamsPeriod };

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

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

			var williamsArea = CreateChartArea();
			if (williamsArea != null)
				DrawIndicator(williamsArea, _williamsR);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal value)
	{
		if (candle.State != CandleStates.Finished || !_williamsR.IsFormed)
			return;

		var signal = GetSignal(value, OversoldLevel, OverboughtLevel);

		if (signal > 0 && Position <= 0m)
		{
			LogInfo("Williams %R {0}: LONG", value);
			BuyMarket(Volume + Math.Abs(Position));
		}
		else if (signal < 0 && Position >= 0m)
		{
			LogInfo("Williams %R {0}: SHORT", value);
			SellMarket(Volume + Math.Abs(Position));
		}
	}

	internal static int GetSignal(decimal williamsR, decimal oversoldLevel, decimal overboughtLevel)
	{
		if (williamsR <= oversoldLevel)
			return 1;

		if (williamsR >= overboughtLevel)
			return -1;

		return 0;
	}
}