在 GitHub 上查看

BW WiseMan-1 突破策略

该策略是 MetaTrader 专家顾问 Exp_BW-wiseMan-1 的 StockSharp 版本。它实现了基于比尔·威廉姆斯鳄鱼指标的 WiseMan-1 突破逻辑。当一根完成的K线脱离鳄鱼三条线并同时突破最近的摆动高/低点时生成信号。可选的逆势模式会交换买卖条件,从而实现对同一突破的反向操作。

核心思想

  • 使用平滑移动平均线计算鳄鱼指标,对应的输入为中位价 (最高价 + 最低价) / 2
  • 将颚线、齿线和唇线按照参数设置向前平移,以匹配原始指标的显示方式。
  • 只有当当前K线突破最近 Back 根K线的最高或最低点时才确认突破,以过滤噪音行情。
  • 通过 SignalBar 参数可以延迟信号,允许在更早完成的K线上执行交易。

交易规则

做多方向

  1. K线的最高价必须 低于 三条鳄鱼线(颚、齿、唇)。
  2. 收盘价位于K线的上半部分,即高于该K线的中位价。
  3. 当前最低价必须严格低于最近 Back 根K线的最低价。
  4. 当信号在等待 SignalBar 根K线之后生效时:
    • 如果启用了“平空”,先平掉所有空头仓位。
    • 若启用了“做多”并且当前没有持仓,则开立新的多单。

做空方向

  1. K线的最低价必须 高于 三条鳄鱼线。
  2. 收盘价位于K线的下半部分,即低于该K线的中位价。
  3. 当前最高价必须高于最近 Back 根K线的最高价。
  4. 当信号生效时:
    • 如果启用了“平多”,先平掉所有多头仓位。
    • 若启用了“做空”并且当前没有持仓,则开立新的空单。

逆势模式

将“逆势模式”设置为 true 会交换买卖条件,使策略在突破方向的反方向上进行交易。

参数说明

  • Candle Type – 用于构建K线并计算指标的周期(默认:1小时)。
  • Counter-Trend Mode – 是否逆势交易,即交换买卖信号(默认:开启,与原始EA一致)。
  • Breakout Depth (Back) – 验证突破时参考的历史K线数量(默认:2)。
  • Jaw Length / Shift – 颚线的平滑移动平均周期与前移距离(默认:13 / 8)。
  • Teeth Length / Shift – 齿线的平滑移动平均周期与前移距离(默认:8 / 5)。
  • Lips Length / Shift – 唇线的平滑移动平均周期与前移距离(默认:5 / 3)。
  • Signal Bar – 信号延迟执行的完成K线数量(默认:1)。
  • Enable Long / Enable Short – 是否允许开多 / 开空。
  • Close Long / Close Short – 信号触发时是否平掉相反方向的仓位。

说明

  • 策略仅使用市价单,不会自动设置止损或止盈;平仓完全依赖于相反信号或关闭相关开仓开关。
  • 所有计算都基于已经完成的K线,忽略盘中尚未完成的数据,以保持与原MT5专家顾问一致。
  • 交易量取自 StockSharp 策略的全局设置,如需调整仓位大小,请在平台配置中修改默认交易量。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bill Williams Wiseman 1 strategy using Alligator-style triple EMA crossover.
/// </summary>
public class BwWiseman1Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _jawPeriod;
	private readonly StrategyParam<int> _teethPeriod;
	private readonly StrategyParam<int> _lipsPeriod;

	private decimal? _prevLips;
	private decimal? _prevTeeth;

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

	public int JawPeriod
	{
		get => _jawPeriod.Value;
		set => _jawPeriod.Value = value;
	}

	public int TeethPeriod
	{
		get => _teethPeriod.Value;
		set => _teethPeriod.Value = value;
	}

	public int LipsPeriod
	{
		get => _lipsPeriod.Value;
		set => _lipsPeriod.Value = value;
	}

	public BwWiseman1Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_jawPeriod = Param(nameof(JawPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("Jaw Period", "Slow EMA (Jaw)", "Indicators");

		_teethPeriod = Param(nameof(TeethPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("Teeth Period", "Medium EMA (Teeth)", "Indicators");

		_lipsPeriod = Param(nameof(LipsPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Lips Period", "Fast EMA (Lips)", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevLips = null;
		_prevTeeth = null;
	}

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

		_prevLips = null;
		_prevTeeth = null;

		var jaw = new SmoothedMovingAverage { Length = JawPeriod };
		var teeth = new SmoothedMovingAverage { Length = TeethPeriod };
		var lips = new SmoothedMovingAverage { Length = LipsPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(jaw, teeth, lips, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevLips = lipsVal;
			_prevTeeth = teethVal;
			return;
		}

		if (_prevLips == null || _prevTeeth == null)
		{
			_prevLips = lipsVal;
			_prevTeeth = teethVal;
			return;
		}

		// Lips crosses above teeth → buy
		if (_prevLips.Value <= _prevTeeth.Value && lipsVal > teethVal)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Lips crosses below teeth → sell
		else if (_prevLips.Value >= _prevTeeth.Value && lipsVal < teethVal)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevLips = lipsVal;
		_prevTeeth = teethVal;
	}
}