在 GitHub 上查看

会合线随机指标策略

概述

会合线随机指标策略 复刻了 MetaTrader 专家顾问 Expert_AML_Stoch 的思想,通过 StockSharp 高层 API 实现。策略利用看涨/看跌会合线蜡烛形态作为方向线索,并使用随机振荡指标的 %D 信号线进行动量确认。借助高阶 API,逻辑保持模块化,便于在 StockSharp Designer 中优化或接入自定义组合管理。

交易逻辑

  1. 蜡烛形态过滤

    • 连续监测最近两根收盘蜡烛是否构成会合线形态。
    • 看涨条件:先出现一根实体较长的阴线,随后一根实体较长的阳线,其收盘价与前一根的收盘价差距不超过平均实体的 10%。
    • 看跌条件:先出现实体较长的阳线,再出现实体较长的阴线,且两根蜡烛的收盘价差距满足同样的 10% 限制。
    • 为避免弱信号,使用可配置的简单移动平均对蜡烛实体长度进行平滑,等价于 MQL 中的 AvgBody 计算。
  2. 随机指标确认

    • 仅使用随机指标的 %D 线进行过滤,与原版 EA 保持一致。
    • 看涨入场需要 %D 低于可调的超卖阈值(默认 30)。
    • 看跌入场要求 %D 高于可调的超买阈值(默认 70)。
  3. 离场与反向

    • 空头仓位在 %D 向上穿越下轨(默认 20)或上轨(默认 80)时平仓。
    • 多头仓位在 %D 向下跌破同样的上下阈值时退出。
    • 当出现反向信号时,策略自动以足够的手数平掉现有仓位并建立新仓,确保持仓方向正确。
  4. 交易量处理

    • Volume 参数为正,则直接使用该值;否则默认使用 1 手,以模拟原 EA 的固定手数资金管理。

参数说明

名称 描述 默认值 备注
CandleType 参与计算的主 K 线数据类型。 15 分钟 可选择任意 DataType,包括不同周期或成交量 K 线。
StochasticLength 原始 %K 计算的回溯周期。 3 对应 MetaTrader 的 %K period
StochasticSmoothing %K 平滑周期(MetaTrader 中的 slowing)。 25 控制内部平滑强度。
StochasticSignal %D 信号线的平滑周期。 36 对应 MetaTrader 的 %D period
BodyAveragePeriod 计算蜡烛实体平均值的周期长度。 3 抑制噪声形态。
LongEntryLevel 看涨入场时允许的最大 %D 数值。 30 等同于超卖阈值。
ShortEntryLevel 看跌入场所需的最小 %D 数值。 70 等同于超买阈值。
ExitLowerLevel 触发离场的下阈值。 20 多空共用,用于检测向上突破。
ExitUpperLevel 触发离场的上阈值。 80 多空共用,用于检测向下跌破。

所有参数都通过 StrategyParam<T> 暴露,既可以在 Designer 中优化,也可以在代码中动态调整。

信号定义

  • 开多:识别到看涨会合线且上一根蜡烛的 %D 低于 LongEntryLevel,若当前无多单则开仓,若存在空单则先平空再开多。
  • 开空:识别到看跌会合线且 %D 高于 ShortEntryLevel,若当前无空单则开仓,若存在多单则先平多再开空。
  • 平多:%D 自上而下穿越 ExitUpperLevelExitLowerLevel
  • 平空:%D 自下而上突破 ExitLowerLevelExitUpperLevel

实现细节

  • 使用 SubscribeCandles + BindEx 将随机指标值直接传递给处理函数,无需手动管理指示器集合。
  • 蜡烛实体平均值通过 SimpleMovingAverageDecimalIndicatorValue 计算,完整复现 MQL 中的 AvgBody 逻辑。
  • 所有源码注释均为英文,缩进严格使用制表符,符合根目录 AGENTS.md 的规范。
  • 若创建了图表区域,策略会自动绘制 K 线与随机指标,方便实时监控。

使用建议

  1. 参数优化:针对不同品种或周期执行滚动优化,调整 %D 阈值和平均周期。
  2. 风险控制:可结合 StockSharp 的 StartProtection 或外部资金管理模块设定止盈/止损。
  3. 数据质量:会合线对开收盘价敏感,建议过滤流动性差或缺口较大的交易时段。
  4. 多周期应用:可将 CandleType 切换为更长或更短周期,以匹配不同交易风格。

通过该策略,交易者可以将传统蜡烛形态与动量确认结合起来,获得更加客观的入场与离场信号。

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Meeting Lines + Stochastic strategy.
/// Buys on bullish meeting lines with low stochastic, sells on bearish meeting lines with high stochastic.
/// </summary>
public class MeetingLinesStochasticStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<decimal> _stochLow;
	private readonly StrategyParam<decimal> _stochHigh;

	private ICandleMessage _prevCandle;
	private ICandleMessage _prevPrevCandle;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
	public decimal StochLow { get => _stochLow.Value; set => _stochLow.Value = value; }
	public decimal StochHigh { get => _stochHigh.Value; set => _stochHigh.Value = value; }

	public MeetingLinesStochasticStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Period", "Stochastic K period", "Indicators");
		_stochLow = Param(nameof(StochLow), 30m)
			.SetDisplay("Stoch Low", "Stochastic oversold level", "Signals");
		_stochHigh = Param(nameof(StochHigh), 70m)
			.SetDisplay("Stoch High", "Stochastic overbought level", "Signals");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCandle = null;
		_prevPrevCandle = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCandle = null;
		_prevPrevCandle = null;
		var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stoch, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
	{
		if (candle.State != CandleStates.Finished) return;

		var stochTyped = stochValue as StochasticOscillatorValue;
		if (stochTyped?.K is not decimal kValue) { UpdateState(candle); return; }

		if (_prevCandle != null && _prevPrevCandle != null)
		{
			var avgBody = (Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice) +
						   Math.Abs(_prevPrevCandle.ClosePrice - _prevPrevCandle.OpenPrice)) / 2m;

			if (avgBody > 0)
			{
				// Bullish meeting lines: prev bearish, current bullish, closes near
				var prevBearish = _prevCandle.OpenPrice > _prevCandle.ClosePrice &&
								  (_prevCandle.OpenPrice - _prevCandle.ClosePrice) > avgBody * 0.5m;
				var currBullish = candle.ClosePrice > candle.OpenPrice &&
								  (candle.ClosePrice - candle.OpenPrice) > avgBody * 0.5m;
				var closesNear = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;

				if (prevBearish && currBullish && closesNear && kValue < StochLow && Position <= 0)
					BuyMarket();

				// Bearish meeting lines: prev bullish, current bearish, closes near
				var prevBullish = _prevCandle.ClosePrice > _prevCandle.OpenPrice &&
								  (_prevCandle.ClosePrice - _prevCandle.OpenPrice) > avgBody * 0.5m;
				var currBearish = candle.OpenPrice > candle.ClosePrice &&
								  (candle.OpenPrice - candle.ClosePrice) > avgBody * 0.5m;
				var closesNear2 = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;

				if (prevBullish && currBearish && closesNear2 && kValue > StochHigh && Position >= 0)
					SellMarket();
			}
		}

		UpdateState(candle);
	}

	private void UpdateState(ICandleMessage candle)
	{
		_prevPrevCandle = _prevCandle;
		_prevCandle = candle;
	}
}