在 GitHub 上查看

Gselector Pattern Probability 策略

概述

Gselector Pattern Probability 策略是 MT4「Gselector」专家的 StockSharp 版本。策略针对多个价格步长构建合成价阶,记录每一个新出现的模式并评估继续运行的概率,只要概率足够高就发出交易信号。止损和止盈由代码主动管理,从而重现原有 EA 的行为。

学习流程

  1. 合成价阶:对每个设定的 delta 倍数,行情每移动一次指定距离就记录最新收盘价。
  2. 模式编码:比较价阶中相邻的数值并生成位掩码。上涨写入 0,下跌写入 1,与 MQL 版本的 Ncomb 逻辑保持一致。
  3. 事件跟踪:当检测到新模式时,为所有止损层级创建观察器。观察器保存起始价格并等待行情向任意方向运行到指定距离。
  4. 概率更新:观察器结束后,如果价格向上突破则增加 Growth 统计,向下突破则增加 Decline,并使用遗忘系数模拟原代码的 forg 衰减。
  5. 会话级缓存:所有统计数据存储在内存中,策略重新启动时会重新学习,相当于原始 EA 在 ReadHistory=0 时的行为。

交易规则

  1. 每根已完成的 K 线都会为当前模式计算多条价阶的延续概率。
  2. 做多信号条件:
    • 概率 ≥ ProbabilityThreshold
    • 完成样本数 ≥ MinSamples
    • 自上一次做多信号以来已等待 CooldownFactor 根 K 线;
    • 如果存在空头仓位,新的概率必须高出上一次卖出概率至少 ProbabilityBuffer
  3. 做空条件完全对称,只是互换 GrowthDecline 的角色。
  4. 开仓使用 BuyMarket / SellMarket,若持有反向仓位会先平仓再反手,模拟 MT4 中的 OrderSend 行为。
  5. 止损与止盈根据点值计算并在代码中主动监控。

参数

参数 说明 默认值
CandleType 策略订阅的 K 线类型。 1 分钟
ProbabilityThreshold 触发交易所需的最小概率。 0.8
BaseDeltaPoints 第一条价阶的基础点数。 1
DeltaSteps 要分析的价阶数量。 20
PatternLength 每条价阶保存的历史长度。 10
StopLevels 止损/止盈层级数。 1
StopDistancePoints 基础止损距离(以点为单位)。 25
ForgetFactor 每次观察后的衰减系数。 1.05
MinSamples 启动交易前需要的最少样本。 10
ProbabilityBuffer 用于平掉反向仓位的附加概率。 0.05
FixedVolume 基础下单手数。 1 手
UseReinvest 是否根据账户权益调整手数。
VolumeMode 0=固定;1=按权益百分比;2=阶梯式;3=线性递增。 1
PercentPer10k 在模式 1 中每 10,000 单位的百分比。 3
BaseDeposit 模式 2 和 3 的初始权益。 500
DepositStep 模式 2 和 3 的权益增量。 500
MaxVolume 手数上限。 10000
CooldownFactor 再次激活同一模式所需的 K 线数量。 2

与 MQL 专家的差异

  • 去掉了文件持久化,统计数据在每次启动时重新计算。
  • 使用 BuyMarket/SellMarket 并在代码中管理止损/止盈,而不是依赖 MT4 的挂单参数。
  • 手数计算改写为使用 StockSharp 投资组合信息,若获取不到权益则回退到固定手数。
  • 原代码中的追踪止损输入在 MT4 中未生效,因此此处同样忽略。

使用建议

  • 交易工具需要提供 PriceStep,否则策略会假设点值为 0.0001。
  • 策略需要时间累积统计数据,初期阶段可能不会立即下单。
  • DeltaStepsPatternLength 的组合越大,占用的内存与计算量越多,请根据环境调整。
  • 默认概率阈值 0.8 较为严格,可根据需求降低以提高交易频率。
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>
/// Gselector Pattern Probability strategy - SMA crossover with RSI filter.
/// Buys when fast SMA crosses above slow SMA and RSI is below overbought.
/// Sells when fast SMA crosses below slow SMA and RSI is above oversold.
/// </summary>
public class GselectorPatternProbabilityStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GselectorPatternProbabilityStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI lookback", "Indicators");

		_overbought = Param(nameof(Overbought), 75m)
			.SetDisplay("Overbought", "RSI overbought level", "Levels");

		_oversold = Param(nameof(Oversold), 25m)
			.SetDisplay("Oversold", "RSI oversold level", "Levels");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		// Fast crosses above slow = buy
		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Fast crosses below slow = sell
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}