在 GitHub 上查看

Gonna Scalp 策略

概述

Gonna Scalp 策略将 MetaTrader 高频剥头皮专家顾问移植到 StockSharp 高级 API。系统在短周期图表上寻找快速的均值回归机会,同时尊重主要趋势。交易前使用投票机制对动量、CCI、ATR、随机指标和 MACD 多重过滤器进行评估。策略同一时间只持有一笔仓位,并为每笔交易设置以 MetaTrader 点数表示的固定止损和止盈。

交易逻辑

  1. 指标准备
    • 以典型价计算的快、慢加权移动平均线(WMA)。
    • 周期为 14 的动量指标,转换为与中性值 100 的绝对距离。
    • 周期为 20 的 CCI 与周期为 12 的 ATR 作为方向过滤。
    • 使用同一根 K 线数据计算的随机指标 %K/%D(5/3/3)以及 MACD(12/26/9)。
  2. 信号投票
    • 每个指标根据与原始 EA 相同的规则,为多头或空头方向投出一票。
    • 策略维护最近三次动量距离,并要求至少有一次超过阈值才允许开仓。
    • 结构过滤要求:做多时,前两根 K 线的最低价必须低于上一根 K 线的最高价;做空时条件相反。
  3. 下单执行
    • 当多头票数大于空头票数且所有过滤器满足条件时,按照设定手数开多单。
    • 当空头票数占优且动量过滤通过时,按照设定手数开空单。
  4. 风险控制
    • 每笔持仓都绑定固定止损和止盈点数,并根据合约最小跳动转换为价格距离。
    • 一旦当前 K 线突破任意一条防护线,策略立即平仓。

主要参数

参数 描述 默认值
TradeVolume 调整后的基础下单手数(手)。 0.01
FastMaPeriod 快速 WMA 周期。 1
SlowMaPeriod 慢速 WMA 周期。 5
MomentumPeriod 动量指标周期。 14
MomentumBuyThreshold 做多时要求的最小动量偏离。 0.3
MomentumSellThreshold 做空时要求的最小动量偏离。 0.3
StopLossSteps 以 MetaTrader 点表示的止损距离。 200
TakeProfitSteps 以 MetaTrader 点表示的止盈距离。 200
CandleType 交易逻辑使用的时间周期(默认 5 分钟)。 M5

使用提示

  • 通过 TradeVolume 参数匹配交易品种的最小手数,策略会自动对齐交易所的手数步长。
  • 止损与止盈参数以 MetaTrader 点为单位,并按照合约精度转换为实际价格距离。
  • 策略至少需要三根完整的 K 线来填充动量历史缓冲区,之后才可能生成信号。
  • 策略不会加仓或网格化,同一时间只有一笔持仓;平仓后才会寻找下一次信号。
  • 可将策略连接到 StockSharp 图表,直观观察 WMA、随机指标与 MACD 曲线帮助确认信号。
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// Gonna Scalp strategy: WMA crossover with CCI momentum filter.
/// Buys when fast WMA above slow WMA and CCI above -100.
/// Sells when fast WMA below slow WMA and CCI below 100.
/// </summary>
public class GonnaScalpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _cciPeriod;

	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 int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	public GonnaScalpStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");

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

		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
	}

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

		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };
		var cci = new CommodityChannelIndex { Length = CciPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, cci, (candle, fastVal, slowVal, cciVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && cciVal > -100m && Position <= 0)
						BuyMarket();
					else if (crossDown && cciVal < 100m && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

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