在 GitHub 上查看

FT Bill Williams AO 策略

概述

FT Bill Williams AO 策略 是 MetaTrader 4 智能交易系统 FT_BillWillams_AO 的 StockSharp 高阶 API 版本。原始程序 由 FORTRADER.RU 发布,结合了 Bill Williams 的分形、Alligator 指标以及 Awesome Oscillator,用于捕捉趋势突破的 早期信号。移植版本保留原有逻辑,同时采用净头寸模型,反向信号会先平掉当前仓位再开立新仓。

策略在每根收盘蜡烛上执行以下流程:

  1. 使用指定的奇数根蜡烛检测看涨与看跌分形。
  2. 检查分形价格是否位于 Alligator 牙线之外。
  3. 按照 SignalShift 参数读取 Awesome Oscillator 的三个历史值,确认加速条件 (A>BB<C 以及正负号)。
  4. 根据上/下突破公式 High[SignalShift] + IndentPoints * 步长Low[SignalShift] - IndentPoints * 步长 计算触发价。
  5. 根据参数选择执行颚线平仓、反向信号平仓以及 Gragus 拖尾规则。

入场规则

多头

  • 检测到看涨分形且其高点高于 Alligator 的牙线。
  • AO 在 SignalShift+2SignalShift+1SignalShift 三根蜡烛上的值均大于 0,且满足 A > BB < C
  • 触发价 = High[SignalShift] + IndentPoints * PriceStep
  • 当前完成蜡烛最高价突破触发价且 AO 仍在上升 (C > B) 时买入,必要时先平空后开多。

空头

  • 检测到看跌分形且其低点低于牙线。
  • AO 三个历史值均小于 0,且 A < BB > C
  • 触发价 = Low[SignalShift] - IndentPoints * PriceStep
  • 当前蜡烛最低价跌破触发价且 AO 继续下降 (C < B) 时做空或翻仓。

出场与风险控制

  • StopLossPointsTakeProfitPoints 以 MetaTrader 点数表示,系统根据品种价格步长换算为实际价差。
  • CloseDropTeeth 控制是否在价格穿越 Alligator 颚线时立即离场,可选择当前收盘或上一根收盘价。
  • CloseReverseSignal 可设置为在出现相反分形或当相反方向的突破信号准备就绪时平仓。
  • 启用 UseTrailing 时执行 Gragus 拖尾:若嘴唇线斜率大于辅助 SMA,则止损跟随嘴唇;否则跟随牙线,两种情况 均要求价格距离目标线至少 12 点。

参数说明

参数 说明
TradeVolume 每次交易的手数,同时赋值给 Strategy.Volume
CandleType 使用的蜡烛数据类型及时间框。
FractalPeriod 分形窗口大小,应为奇数(默认 5)。
IndentPoints 突破触发价的额外点数偏移。
JawPeriod / TeethPeriod / LipsPeriod Alligator 下颚、牙齿、嘴唇的平滑均线长度。
JawShift / TeethShift / LipsShift 对应均线的前移位数(单位:蜡烛)。
CloseDropTeeth 颚线平仓模式:关闭、当前收盘穿越、上一收盘穿越。
CloseReverseSignal 反向信号离场设置:关闭、出现相反分形、相反突破信号就绪。
UseTrailing 是否启用 Gragus 拖尾逻辑。
TrendSmaPeriod 拖尾时使用的辅助 SMA 周期。
StopLossPoints 初始止损点数,设为 0 可禁用。
TakeProfitPoints 初始止盈点数,设为 0 可禁用。
SignalShift 读取 AO 与高/低价时回看的已完成蜡烛数量。

其他注意事项

  • 策略优先使用 PriceStep,若不可用则退回 MinPriceStep,最后使用默认步长 0.0001
  • 采用净头寸模型,同方向不会累加订单;翻向时会先平仓再开新仓。
  • 为保持原版表现,请使用奇数的 FractalPeriod(默认 5)。
  • IndentPointsStopLossPointsTakeProfitPoints 都以 MetaTrader 点数计量,需结合品种精度调整。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bill Williams Awesome Oscillator strategy.
/// Buys when AO crosses above zero and sells when AO crosses below zero,
/// filtered by Alligator teeth alignment.
/// </summary>
public class FtBillWilliamsAoStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _jawPeriod;
	private readonly StrategyParam<int> _teethPeriod;
	private readonly StrategyParam<int> _lipsPeriod;

	private decimal _prevAo;
	private decimal _prevTeeth;
	private bool _isReady;

	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 FtBillWilliamsAoStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for strategy", "General");

		_jawPeriod = Param(nameof(JawPeriod), 13)
			.SetDisplay("Jaw Period", "Alligator jaw SMA period", "Alligator");

		_teethPeriod = Param(nameof(TeethPeriod), 8)
			.SetDisplay("Teeth Period", "Alligator teeth SMA period", "Alligator");

		_lipsPeriod = Param(nameof(LipsPeriod), 5)
			.SetDisplay("Lips Period", "Alligator lips SMA period", "Alligator");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevAo = 0;
		_prevTeeth = 0;
		_isReady = false;
	}

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

		var ao = new AwesomeOscillator();
		var teeth = new SMA { Length = TeethPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ao, teeth, OnProcess)
			.Start();

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

	private void OnProcess(ICandleMessage candle, decimal aoVal, decimal teethVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_isReady)
		{
			_prevAo = aoVal;
			_prevTeeth = teethVal;
			_isReady = true;
			return;
		}

		var close = candle.ClosePrice;

		// Buy signal: AO crosses above zero, price above teeth
		if (_prevAo <= 0 && aoVal > 0 && close > teethVal)
		{
			if (Position < 0)
				BuyMarket(); // close short

			if (Position <= 0)
				BuyMarket();
		}
		// Sell signal: AO crosses below zero, price below teeth
		else if (_prevAo >= 0 && aoVal < 0 && close < teethVal)
		{
			if (Position > 0)
				SellMarket(); // close long

			if (Position >= 0)
				SellMarket();
		}

		_prevAo = aoVal;
		_prevTeeth = teethVal;
	}
}