在 GitHub 上查看

SMC Trader Camel CCI MACD 策略

概述

本策略为 MetaTrader 4 指标 “Steve Cartwright Trader Camel CCI MACD” 的 StockSharp 版本。 策略复刻了原始 EA 的核心逻辑:使用基于最高价/最低价的 EMA 通道、MACD 趋势过滤以及 CCI 阈值判断。 所有交易决策都在完成的 K 线之后执行,以保持与 MQL4 中逐 K 检查的行为一致。

交易逻辑

  1. 指标组件
    • 两条相同周期的指数移动平均线 (EMA) 分别作用于 K 线最高价和最低价,形成 Camel 通道。 前一根 K 线的收盘价突破该通道的上沿或下沿意味着动能增强。
    • 标准 MACD (快线 EMA、慢线 EMA、信号线) 用于确认趋势方向。
    • CCI 指标利用 ±100 的阈值检验动能强度。
  2. 做多条件
    • 前一根 K 线收盘价高于 Camel 通道上轨。
    • 前一根 K 线的 MACD 主线大于 0 且高于信号线。
    • 前一根 K 线的 CCI 高于正向阈值。
    • 当前无持仓,且上一次离场到现在至少过去一个 K 线周期。
  3. 做空条件
    • 前一根 K 线收盘价低于 Camel 通道下轨。
    • 前一根 K 线的 MACD 主线小于 0 且低于信号线。
    • 前一根 K 线的 CCI 低于负向阈值。
    • 同样要求空仓并满足冷却时间。
  4. 离场规则
    • 多单:当前一根 K 线的 MACD 主线跌破信号线,或 CCI 回落至阈值以下时平仓。
    • 空单:当前一根 K 线的 MACD 主线上穿信号线时平仓。
    • 每次平仓后都会记录离场时间,在一个 K 线周期内禁止再次开仓。

由于全部判断都基于前一根 K 线的数据,策略每根 K 线最多只会触发一笔交易。

参数

参数 说明 默认值
CandleType 用于计算指标的蜡烛类型与周期。 1 小时 K 线
CamelLength Camel 通道的 EMA 周期。 34
CciPeriod CCI 指标周期。 20
MacdFastPeriod MACD 快速 EMA 周期。 12
MacdSlowPeriod MACD 慢速 EMA 周期。 26
MacdSignalPeriod MACD 信号线平滑周期。 9
CciThreshold CCI 入场阈值,正负方向对称使用。 100

所有参数均已配置 SetOptimize,可直接在 StockSharp 优化器中进行网格搜索。

风险管理

  • 下单通过 BuyMarketSellMarket 方法执行,默认使用策略的 Volume 设置。
  • StartProtection() 被调用以启用 StockSharp 的通用保护机制。
  • 原版 EA 未使用固定止损/止盈,本策略同样仅依赖指标信号离场。

图表展示

策略会在图表上绘制 Camel EMA 通道、MACD、CCI 以及自己的成交记录,帮助还原原始 EA 的视觉提示。

其他说明

  • 冷却时间基于 CandleType.Arg 中的 TimeSpan 计算;切换周期时请确认该参数有效。
  • 使用前一根 K 线数据的方式,与 MQL4 中调用 iMACDiCCIiMA 并传入 shift = 1 的效果保持一致。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// Strategy combining CCI and MACD signal crossover with EMA trend filter.
/// Buy when MACD crosses above signal with CCI positive and price above EMA.
/// Sell when MACD crosses below signal with CCI negative and price below EMA.
/// </summary>
public class SmcTraderCamelCciMacd1Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _macdSignalPeriod;
	private readonly StrategyParam<int> _cciPeriod;

	private decimal? _prevMacdMain;
	private decimal? _prevMacdSignal;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int MacdFastPeriod { get => _macdFastPeriod.Value; set => _macdFastPeriod.Value = value; }
	public int MacdSlowPeriod { get => _macdSlowPeriod.Value; set => _macdSlowPeriod.Value = value; }
	public int MacdSignalPeriod { get => _macdSignalPeriod.Value; set => _macdSignalPeriod.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }

	public SmcTraderCamelCciMacd1Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_emaLength = Param(nameof(EmaLength), 34)
			.SetDisplay("EMA Length", "Trend EMA period", "Indicators");

		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
			.SetDisplay("MACD Fast", "Fast EMA for MACD", "Indicators");

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
			.SetDisplay("MACD Slow", "Slow EMA for MACD", "Indicators");

		_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
			.SetDisplay("MACD Signal", "Signal line period", "Indicators");

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

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

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

		_prevMacdMain = null;
		_prevMacdSignal = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevMacdMain = null;
		_prevMacdSignal = null;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFastPeriod },
				LongMa = { Length = MacdSlowPeriod }
			},
			SignalMa = { Length = MacdSignalPeriod }
		};
		var cci = new CommodityChannelIndex { Length = CciPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, cci, ema, ProcessCandle)
			.Start();
	}

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

		if (!macdValue.IsFinal || !cciValue.IsFinal || !emaValue.IsFinal)
			return;

		if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdData)
			return;

		if (macdData.Macd is not decimal macdMain || macdData.Signal is not decimal macdSignal)
			return;

		var cci = cciValue.ToDecimal();
		var emaVal = emaValue.ToDecimal();

		if (_prevMacdMain is not decimal prevMain || _prevMacdSignal is not decimal prevSignal)
		{
			_prevMacdMain = macdMain;
			_prevMacdSignal = macdSignal;
			return;
		}

		var macdBullCross = prevMain <= prevSignal && macdMain > macdSignal;
		var macdBearCross = prevMain >= prevSignal && macdMain < macdSignal;

		// Long: MACD bullish cross + CCI > 0 + price above EMA
		if (Position <= 0 && macdBullCross && cci > 0 && candle.ClosePrice > emaVal)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Short: MACD bearish cross + CCI < 0 + price below EMA
		else if (Position >= 0 && macdBearCross && cci < 0 && candle.ClosePrice < emaVal)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMacdMain = macdMain;
		_prevMacdSignal = macdSignal;
	}
}