在 GitHub 上查看

AO Lightning 策略

概述

AO Lightning 使用 StockSharp 的高级 API 复刻 MT5 专家顾问 “AO_Lightning”。策略跟踪基于中值价格的 Awesome Oscillator (AO) 斜率:当 AO 下降时逐步加仓多头,而当 AO 上升时逐步建立空头。头寸以分层方式累积,并受最大层数限制;方向切换前会先平掉反向仓位。

交易逻辑

  1. 订阅所选的蜡烛序列,并以默认的短周期 5、长周期 34 计算 Awesome Oscillator(与原始 MQL 代码一致)。
  2. 仅处理收盘完成的蜡烛,以避免重复触发。
  3. 第一根完成的蜡烛仅用于存储参考 AO 值。
  4. 当当前 AO 值 低于 前一根蜡烛时:
    • 如果存在空头仓位,发送市价买单,既平掉全部空头又同时新增一层多头。
    • 如果当前没有空头且多头仓位尚未达到上限,则再买入一层。
  5. 当当前 AO 值 高于 前一根蜡烛时:
    • 如果存在多头仓位,发送市价卖单,既平掉全部多头又同时新增一层空头。
    • 如果当前没有多头且空头仓位尚未达到上限,则再卖出一层。
  6. AO 值与上一根相等时不做操作。
  7. 启动时调用 StartProtection(),方便在 Designer 中附加止损等保护模块。

该流程与原始专家顾问一致:AO 斜率决定方向,翻转前会先平仓,随后按照设定的层数线性加仓。

仓位管理

  • TradeVolume 决定每次加仓的数量,对应 MT5 中的 LotFixed 参数。
  • MaxPositions 对应 MT5 的 Orders 输入,用于限制每个方向的最大层数。
  • 分层加仓 为线性方式,只要未达上限,每个有效信号增加一层相同的手数。
  • 方向翻转 通过单一的市价单完成,同时平掉旧仓并建立新方向的首层仓位。

参数

名称 说明 默认值
TradeVolume 每层加仓的下单数量。 1
MaxPositions 任一方向允许的最大层数。 10
AoShortPeriod Awesome Oscillator 中的快速 SMA 周期(基于中值价格)。 5
AoLongPeriod Awesome Oscillator 的慢速 SMA 周期。 34
CandleType 策略使用的蜡烛类型。 5 分钟周期

备注

  • 原 MT5 专家顾问使用 Period_sma_slowPeriod_sma_fast 作为输入,但数值 5 和 34 实际上互换。该移植版本以 AoShortPeriod/AoLongPeriod 直观地暴露参数,同时保持相同行为。
  • 根据任务要求,此次未提供 Python 版本。
  • 仓库未附带测试,请在投入真实环境前通过 Designer 或自有回测平台进行验证。
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// AO Lightning strategy.
/// Trades based on the Awesome Oscillator momentum slope direction.
/// Buys when AO is rising, sells when AO is falling.
/// </summary>
public class AoLightningStrategy : Strategy
{
	private readonly StrategyParam<int> _aoShortPeriod;
	private readonly StrategyParam<int> _aoLongPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevAo;
	private bool _initialized;

	public int AoShortPeriod
	{
		get => _aoShortPeriod.Value;
		set => _aoShortPeriod.Value = value;
	}

	public int AoLongPeriod
	{
		get => _aoLongPeriod.Value;
		set => _aoLongPeriod.Value = value;
	}

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

	public AoLightningStrategy()
	{
		_aoShortPeriod = Param(nameof(AoShortPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("AO Fast", "Short SMA period for Awesome Oscillator", "Indicators");

		_aoLongPeriod = Param(nameof(AoLongPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("AO Slow", "Long SMA period for Awesome Oscillator", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Source candles", "General");
	}

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

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

		_prevAo = 0;
		_initialized = false;

		var ao = new AwesomeOscillator
		{
			ShortMa = { Length = AoShortPeriod },
			LongMa = { Length = AoLongPeriod }
		};

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (!_initialized)
		{
			_prevAo = aoValue;
			_initialized = true;
			return;
		}

		// Buy when AO is rising (bullish momentum)
		if (aoValue > _prevAo && Position <= 0)
		{
			BuyMarket();
		}
		// Sell when AO is falling (bearish momentum)
		else if (aoValue < _prevAo && Position >= 0)
		{
			SellMarket();
		}

		_prevAo = aoValue;
	}
}