在 GitHub 上查看

L3H3 枢轴策略

概述

L3H3 枢轴策略 是 MetaTrader 指标 "L3_H3_Expert" 的 StockSharp 移植版本。原始脚本通过上一交易时段的高、低、收盘价构建日枢轴结构,并在关键价位附近挂出两个挂单,以捕捉突破或回调。移植后的版本保持这一思想:每当高周期(默认:日线)蜡烛收盘,就重新计算枢轴区间,并根据当前价格位于昨日区间的什么位置来决定使用止损单还是限价单。

交易逻辑

  1. 会话统计

    • 每根枢轴蜡烛收盘后(默认:日线),策略会记录上一交易时段的开盘价、高点、低点和收盘价。
    • 使用公式 (High + Low + Close) / 3 计算经典枢轴价位。
    • 这些水平在整个下一交易日内保持有效。
  2. 入场设置

    • 多单挂价设置在昨日低点上方,偏移量等于 EntryOffsetPips 参数(以点差为单位)。
    • 空单挂价设置在昨日高点,与原始脚本一致,不额外添加缓冲。
    • 当监控蜡烛(默认:5 分钟)确认进入新交易日时,策略会布置新的挂单:
      • 如果当前价格低于昨日低点,则挂出买入止损单,以捕捉向上的突破。
      • 如果当前价格高于昨日高点,则挂出卖出止损单,以参与向下的反转。
      • 否则,在相同价位放置限价单,尝试在回调中买入或逢高做空。
    • 止损距离为 StopLossPips,与 MQL 版本固定 16 点的缓冲保持一致。
    • 多空挂单的止盈均设置在枢轴价位。
  3. 订单管理

    • 每次重新计算枢轴后,会撤销所有未成交挂单并使用新价位重新下单。
    • 当进入新交易日时,旧挂单同样会被取消,以防止挂单累积。
    • 挂单成交后,对应的内部引用会被清理,避免重复撤单。

参数

名称 描述 默认值
EntryCandleType 用于监控当前交易日并触发下单的蜡烛类型。 5 分钟
PivotCandleType 用来统计上一交易日数据的高周期蜡烛类型。 日线
EntryOffsetPips 多单挂价相对于昨日低点的点差偏移。 2
StopLossPips 止损距离相对于参考高/低点的点差。 16

与 MQL 指标的差异

  • 原脚本通过不同的交易时段(亚洲、伦敦、纽约)和 Magic Number 控制行为。移植版本使用可配置的高周期蜡烛(默认日线)来统一计算枢轴,更易于在不同券商环境中复现。
  • MQL 使用即时买卖价判断使用止损单还是限价单;StockSharp 版本改用 EntryCandleType 序列的最新收盘价触发逻辑,使事件流更加清晰。
  • 平台相关的订单备注和 Magic Number 不再使用,策略直接维护自身挂单的引用。

使用提示

  • 运行前请确认标的证券提供有效的 PriceStep(最小价格变动)。如果缺失,该策略会在启动时抛出异常。
  • 若想更贴近原始时段划分,可将 PivotCandleType 设置为小时级别并结合聚合器计算,再配合自定义偏移与止损。
  • 在真实账户中部署挂单策略时,请考虑券商的最小距离和挂单有效期等限制。
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>
/// L3/H3 Pivot strategy - trades around Highest/Lowest channel midpoint.
/// Buys when close crosses above the midpoint, sells when below.
/// Uses a longer lookback to simulate daily pivot levels.
/// </summary>
public class L3H3PivotStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;

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

	public L3H3PivotStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 48)
			.SetDisplay("Channel Period", "Lookback for pivot channel", "Indicators");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0m;
		_prevMid = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;
		var mid = (high + low) / 2m;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevMid = mid;
			_hasPrev = true;
			return;
		}

		// Cross above midpoint
		if (_prevClose <= _prevMid && close > mid && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Cross below midpoint
		else if (_prevClose >= _prevMid && close < mid && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMid = mid;
	}
}