在 GitHub 上查看

多周期交易者

该策略监控九个不同周期的K线,并为每个周期计算同一长度的线性回归通道。H1回归通道的斜率用于判断趋势方向:斜率为负时只允许做空,斜率为正时只允许做多。M5与M1通道负责在支撑/阻力附近给出精确的入场位置。

交易流程

  • 数据来源:M1、M5、M15、M30、H1、H4、D1、W1、MN1九个周期的标准K线。
  • 指标:每个周期使用线性回归通道,通道宽度由窗口内收盘价相对回归线的最大偏差确定,形成对称的上下轨道。
  • 趋势过滤:读取H1通道的斜率,只在负斜率下寻找空头信号,在正斜率下寻找多头信号。
  • 入场条件
    • 空头——最近一根M5高点和M1高点同时突破各自通道上轨,并且H1斜率为负。
    • 多头——最近一根M5低点和M1低点同时触及各自通道下轨,并且H1斜率为正。
  • 下单方式:使用设定的交易量发送市价单。止损取自M5通道半宽(上轨或下轨与中心线的一半距离),止盈为M5通道的中心线。
  • 离场:在每根M1K线收盘时检查价格是否到达止损或止盈,满足任一条件即平仓。
  • 仓位控制:策略在任何时刻只持有一个方向的仓位。

参数

参数 说明
EnableTrading 是否允许策略发送订单。
BarsToCount 所有回归通道的计算窗口长度(默认50)。
Volume 每次市价单的交易量(手数)。

备注

  • 增大BarsToCount可使回归线更加平滑,但响应速度会下降。
  • 多周期斜率信息有助于监控更高周期的一致性,尽管只有H1斜率用于决策。
  • 每根新的M5K线都会重新计算止损和止盈,从而使风险控制紧贴当前通道宽度。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Linear regression channel strategy.
/// Uses LinearReg as the center line with Highest/Lowest to form a channel.
/// Sells at upper channel, buys at lower channel, with trend filter from regression slope.
/// </summary>
public class MultiTimeFrameRegressionStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _regressionLength;
	private readonly StrategyParam<int> _channelLength;

	private decimal _prevLrValue;
	private bool _hasPrev;

	public MultiTimeFrameRegressionStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_regressionLength = Param(nameof(RegressionLength), 20)
			.SetDisplay("Regression Length", "Period for linear regression.", "Indicators");

		_channelLength = Param(nameof(ChannelLength), 20)
			.SetDisplay("Channel Length", "Period for highest/lowest channel.", "Indicators");
	}

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

	public int RegressionLength
	{
		get => _regressionLength.Value;
		set => _regressionLength.Value = value;
	}

	public int ChannelLength
	{
		get => _channelLength.Value;
		set => _channelLength.Value = value;
	}

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

		_prevLrValue = 0;
		_hasPrev = false;
	}

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

		_prevLrValue = 0;
		_hasPrev = false;

		var lr = new LinearReg { Length = RegressionLength };
		var highest = new Highest { Length = ChannelLength };
		var lowest = new Lowest { Length = ChannelLength };

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

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

	private void ProcessCandle(ICandleMessage candle, decimal lrValue, decimal highestValue, decimal lowestValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;

		// Determine slope direction from regression
		var slope = _hasPrev ? lrValue - _prevLrValue : 0m;

		// Channel boundaries
		var channelMid = (highestValue + lowestValue) / 2m;
		var channelWidth = highestValue - lowestValue;

		if (channelWidth <= 0)
		{
			_prevLrValue = lrValue;
			_hasPrev = true;
			return;
		}

		// Upper/lower thresholds
		var upperThreshold = channelMid + channelWidth * 0.4m;
		var lowerThreshold = channelMid - channelWidth * 0.4m;

		// Exit conditions
		if (Position > 0 && (close >= upperThreshold || slope < 0))
		{
			SellMarket();
		}
		else if (Position < 0 && (close <= lowerThreshold || slope > 0))
		{
			BuyMarket();
		}

		// Entry conditions
		if (Position == 0)
		{
			if (close <= lowerThreshold && slope >= 0)
			{
				// Price near lower channel with flat/rising regression
				BuyMarket();
			}
			else if (close >= upperThreshold && slope <= 0)
			{
				// Price near upper channel with flat/falling regression
				SellMarket();
			}
		}

		_prevLrValue = lrValue;
		_hasPrev = true;
	}
}