在 GitHub 上查看

Absolutely No Lag LWMA 策略

概述

该策略复刻 MetaTrader 专家顾问 Exp_AbsolutelyNoLagLwma 的交易思想。策略对所选周期的K线数据连续应用两次线性加权移动平均(LWMA),并根据平滑后的数值斜率对趋势进行着色:2 代表向上,1 表示持平,0 表示向下。颜色的变化触发买入或卖出信号。StockSharp 实现基于高级 API,自动订阅指定时间框架的K线并根据信号发送市价单。

交易逻辑

指标流程

  1. 按照“价格类型”参数选择用于计算的价格序列。
  2. 对价格应用第一个 LWMA,长度由“LWMA 长度”控制。
  3. 使用相同长度的第二个 LWMA 对结果再次平滑。
  4. 将当前平滑值与前一值比较以确定颜色:
    • 2(上涨) – 当前值高于上一值。
    • 1(横盘) – 当前值等于上一值。
    • 0(下跌) – 当前值低于上一值。

信号判定

  • 仅处理已经完成的K线。信号K线参数决定向历史回溯的距离(1 表示最近一根完成K线,2 表示再往前一根,依此类推)。策略同时记录目标K线之前一根K线的颜色,以避免重复入场。
  • 看多转换:目标K线颜色为 2,且其前一根颜色不是 2。允许时开多,并平掉所有空单。
  • 看空转换:目标K线颜色为 0,且其前一根颜色不是 0。允许时开空,并平掉所有多单。

仓位管理

  • 采用市价单执行。当方向反转时,订单数量为 Volume + |Position|,确保旧持仓被完全反向。
  • 多、空入场与平仓可分别启用或禁用,便于实现仅入场或仅离场的模式。
  • 策略启动时调用 StartProtection(),以启用 StockSharp 通用的保护性逻辑。

参数说明

  • LWMA Length(LWMA 长度) – 两个 LWMA 的周期长度。
  • Price Type(价格类型) – 用于计算的价格数据(收盘价、开盘价、最高价、最低价、Median、Typical、Weighted、Simpl、Quarter、TrendFollow 系列、Demark 价格等)。
  • Signal Bar(信号K线) – 回溯多少根完成K线来生成信号。
  • Enable Long Entries(允许多头入场) – 看多信号触发时是否开多。
  • Enable Short Entries(允许空头入场) – 看空信号触发时是否开空。
  • Enable Long Exits(允许多头离场) – 指标转空时是否平多。
  • Enable Short Exits(允许空头离场) – 指标转多时是否平空。
  • Candle Type(K线类型) – 指标使用的时间框架。
  • Volume(基础属性) – 新开仓时使用的数量。

其他说明

  • 默认时间框架为4小时,与原版 EA 设置一致,可通过参数自由调整。
  • 策略不主动下达止盈或止损单,如需风险控制可结合 StockSharp 其他组件使用。
  • 根据需求暂不提供 Python 版本。
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// Absolutely No Lag LWMA strategy (simplified).
/// Uses two EMAs of different periods to detect trend direction changes.
/// </summary>
public class AbsolutelyNoLagLwmaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

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

		_fastPeriod = Param(nameof(FastPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };

		decimal prevFast = 0, prevSlow = 0;
		bool hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, (ICandleMessage candle, decimal fastValue, decimal slowValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevFast = fastValue;
					prevSlow = slowValue;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevFast = fastValue;
					prevSlow = slowValue;
					return;
				}

				if (prevFast <= prevSlow && fastValue > slowValue && Position <= 0)
				{
					BuyMarket();
				}
				else if (prevFast >= prevSlow && fastValue < slowValue && Position >= 0)
				{
					SellMarket();
				}

				prevFast = fastValue;
				prevSlow = slowValue;
			})
			.Start();

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