SMC Trader Camel CCI MACD 策略
概述
本策略为 MetaTrader 4 指标 “Steve Cartwright Trader Camel CCI MACD” 的 StockSharp 版本。 策略复刻了原始 EA 的核心逻辑:使用基于最高价/最低价的 EMA 通道、MACD 趋势过滤以及 CCI 阈值判断。 所有交易决策都在完成的 K 线之后执行,以保持与 MQL4 中逐 K 检查的行为一致。
交易逻辑
- 指标组件
- 两条相同周期的指数移动平均线 (EMA) 分别作用于 K 线最高价和最低价,形成 Camel 通道。 前一根 K 线的收盘价突破该通道的上沿或下沿意味着动能增强。
- 标准 MACD (快线 EMA、慢线 EMA、信号线) 用于确认趋势方向。
- CCI 指标利用 ±100 的阈值检验动能强度。
- 做多条件
- 前一根 K 线收盘价高于 Camel 通道上轨。
- 前一根 K 线的 MACD 主线大于 0 且高于信号线。
- 前一根 K 线的 CCI 高于正向阈值。
- 当前无持仓,且上一次离场到现在至少过去一个 K 线周期。
- 做空条件
- 前一根 K 线收盘价低于 Camel 通道下轨。
- 前一根 K 线的 MACD 主线小于 0 且低于信号线。
- 前一根 K 线的 CCI 低于负向阈值。
- 同样要求空仓并满足冷却时间。
- 离场规则
- 多单:当前一根 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 优化器中进行网格搜索。
风险管理
- 下单通过
BuyMarket与SellMarket方法执行,默认使用策略的Volume设置。 StartProtection()被调用以启用 StockSharp 的通用保护机制。- 原版 EA 未使用固定止损/止盈,本策略同样仅依赖指标信号离场。
图表展示
策略会在图表上绘制 Camel EMA 通道、MACD、CCI 以及自己的成交记录,帮助还原原始 EA 的视觉提示。
其他说明
- 冷却时间基于
CandleType.Arg中的TimeSpan计算;切换周期时请确认该参数有效。 - 使用前一根 K 线数据的方式,与 MQL4 中调用
iMACD、iCCI、iMA并传入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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
CommodityChannelIndex,
ExponentialMovingAverage,
MovingAverageConvergenceDivergenceSignal
)
from StockSharp.Algo.Strategies import Strategy
class smc_trader_camel_cci_macd1_strategy(Strategy):
"""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."""
def __init__(self):
super(smc_trader_camel_cci_macd1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._ema_length = self.Param("EmaLength", 34) \
.SetDisplay("EMA Length", "Trend EMA period", "Indicators")
self._macd_fast_period = self.Param("MacdFastPeriod", 12) \
.SetDisplay("MACD Fast", "Fast EMA for MACD", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26) \
.SetDisplay("MACD Slow", "Slow EMA for MACD", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9) \
.SetDisplay("MACD Signal", "Signal line period", "Indicators")
self._cci_period = self.Param("CciPeriod", 20) \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._prev_macd_main = None
self._prev_macd_signal = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@property
def MacdSignalPeriod(self):
return self._macd_signal_period.Value
@property
def CciPeriod(self):
return self._cci_period.Value
def OnReseted(self):
super(smc_trader_camel_cci_macd1_strategy, self).OnReseted()
self._prev_macd_main = None
self._prev_macd_signal = None
def OnStarted2(self, time):
super(smc_trader_camel_cci_macd1_strategy, self).OnStarted2(time)
self._prev_macd_main = None
self._prev_macd_signal = None
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self.MacdFastPeriod
macd.Macd.LongMa.Length = self.MacdSlowPeriod
macd.SignalMa.Length = self.MacdSignalPeriod
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, cci, ema, self._process_candle).Start()
def _process_candle(self, candle, macd_value, cci_value, ema_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal or not cci_value.IsFinal or not ema_value.IsFinal:
return
macd_raw = macd_value.Macd if hasattr(macd_value, 'Macd') else None
signal_raw = macd_value.Signal if hasattr(macd_value, 'Signal') else None
if macd_raw is None or signal_raw is None:
return
macd_main = float(macd_raw)
macd_signal = float(signal_raw)
cci_val = float(cci_value)
ema_val = float(ema_value)
if self._prev_macd_main is None or self._prev_macd_signal is None:
self._prev_macd_main = macd_main
self._prev_macd_signal = macd_signal
return
prev_main = self._prev_macd_main
prev_signal = self._prev_macd_signal
macd_bull_cross = prev_main <= prev_signal and macd_main > macd_signal
macd_bear_cross = prev_main >= prev_signal and macd_main < macd_signal
close = float(candle.ClosePrice)
# Long: MACD bullish cross + CCI > 0 + price above EMA
if self.Position <= 0 and macd_bull_cross and cci_val > 0 and close > ema_val:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Short: MACD bearish cross + CCI < 0 + price below EMA
elif self.Position >= 0 and macd_bear_cross and cci_val < 0 and close < ema_val:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_macd_main = macd_main
self._prev_macd_signal = macd_signal
def CreateClone(self):
return smc_trader_camel_cci_macd1_strategy()