在 GitHub 上查看
晨星/暮星 CCI 策略
概述
该策略基于 MetaTrader 5 的 Expert_AMS_ES_CCI 智能交易系统,在 StockSharp 高级 API 上复刻其逻辑。策略通过识别“晨星”和“暮星”三根蜡烛的反转形态,并结合商品通道指数(CCI)的确认信号,在主图表的已完成K线上做出交易决策。
交易逻辑
- 晨星形态做多
- 连续三根K线满足晨星特征:
- 第一根:实体明显向下(实体长度大于平均实体长度)。
- 第二根:实体很短,并且整体低于第一根K线。
- 第三根:收盘价高于第一根K线实体的中点。
- 信号柱的 CCI 数值低于负的入场阈值(默认 −50)。
- 暮星形态做空
- 连续三根K线满足暮星特征:
- 第一根:实体明显向上。
- 第二根:实体很短,并且整体高于第一根K线。
- 第三根:收盘价低于第一根K线实体的中点。
- 信号柱的 CCI 数值高于正的入场阈值(默认 +50)。
- 离场条件
- 空头持仓:当 CCI 向上穿越 −NeutralThreshold 或向下穿越 +NeutralThreshold(默认 ±80)时平仓。
- 多头持仓:当 CCI 向下穿越 +NeutralThreshold 或跌破 −NeutralThreshold 时平仓。
- 策略未内置止损/止盈,可结合外部风控模块使用。
指标
- Commodity Channel Index (CCI):确认趋势强度,默认周期 25。
- 蜡烛实体长度的简单移动平均:计算最近 BodyAveragePeriod 根K线的平均实体长度(默认 5),用于验证形态强度。
参数
| 名称 |
说明 |
默认值 |
备注 |
CciPeriod |
CCI 指标计算周期。 |
25 |
可参与优化。 |
BodyAveragePeriod |
计算平均实体长度的窗口。 |
5 |
可参与优化。 |
EntryThreshold |
入场所需的 CCI 绝对值。 |
50 |
取正值,策略会比较 ±EntryThreshold。 |
NeutralThreshold |
确认离场的 CCI 绝对阈值。 |
80 |
取正值,策略会比较 ±NeutralThreshold。 |
CandleType |
参与计算的K线类型或周期。 |
1 小时时间框架 |
可根据需求调整。 |
其他说明
- 策略通过
SubscribeCandles 订阅K线,并利用 Bind 同步获取指标数值。
- 交易指令使用
BuyMarket 与 SellMarket 市价单完成。
- 代码中的注释全部为英文,符合项目要求。
- 如需额外风控,可结合
StartProtection 或自定义资金管理逻辑。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Morning/Evening Star + CCI strategy.
/// Buys on morning star with negative CCI, sells on evening star with positive CCI.
/// </summary>
public class MorningEveningStarCciStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLevel;
private ICandleMessage _prevCandle;
private ICandleMessage _prevPrevCandle;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
public MorningEveningStarCciStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
_cciLevel = Param(nameof(CciLevel), 0m)
.SetDisplay("CCI Level", "CCI threshold for confirmation", "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 cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
if (_prevCandle != null && _prevPrevCandle != null)
{
var prevBody = Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice);
var prevRange = _prevCandle.HighPrice - _prevCandle.LowPrice;
var isSmallBody = prevRange > 0 && prevBody < prevRange * 0.3m;
var firstBearish = _prevPrevCandle.OpenPrice > _prevPrevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var isMorningStar = firstBearish && isSmallBody && currBullish &&
candle.ClosePrice > _prevPrevCandle.OpenPrice * 0.5m + _prevPrevCandle.ClosePrice * 0.5m;
var firstBullish = _prevPrevCandle.ClosePrice > _prevPrevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var isEveningStar = firstBullish && isSmallBody && currBearish &&
candle.ClosePrice < _prevPrevCandle.OpenPrice * 0.5m + _prevPrevCandle.ClosePrice * 0.5m;
if (isMorningStar && cciValue < -CciLevel && Position <= 0)
BuyMarket();
else if (isEveningStar && cciValue > CciLevel && Position >= 0)
SellMarket();
}
_prevPrevCandle = _prevCandle;
_prevCandle = candle;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class morning_evening_star_cci_strategy(Strategy):
"""
Morning/Evening Star + CCI: buy on morning star with negative CCI, sell on evening star with positive CCI.
"""
def __init__(self):
super(morning_evening_star_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14).SetDisplay("CCI Period", "CCI period", "Indicators")
self._cci_level = self.Param("CciLevel", 0.0).SetDisplay("CCI Level", "CCI threshold", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_candle = None
self._prev_prev_candle = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(morning_evening_star_cci_strategy, self).OnReseted()
self._prev_candle = None
self._prev_prev_candle = None
def OnStarted2(self, time):
super(morning_evening_star_cci_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _process_candle(self, candle, cci_val):
if candle.State != CandleStates.Finished:
return
cci = float(cci_val)
cci_level = float(self._cci_level.Value)
if self._prev_candle is not None and self._prev_prev_candle is not None:
prev_body = abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))
prev_range = float(self._prev_candle.HighPrice) - float(self._prev_candle.LowPrice)
is_small_body = prev_range > 0 and prev_body < prev_range * 0.3
pp_open = float(self._prev_prev_candle.OpenPrice)
pp_close = float(self._prev_prev_candle.ClosePrice)
c_open = float(candle.OpenPrice)
c_close = float(candle.ClosePrice)
first_bearish = pp_open > pp_close
curr_bullish = c_close > c_open
is_morning = first_bearish and is_small_body and curr_bullish and c_close > pp_open * 0.5 + pp_close * 0.5
first_bullish = pp_close > pp_open
curr_bearish = c_open > c_close
is_evening = first_bullish and is_small_body and curr_bearish and c_close < pp_open * 0.5 + pp_close * 0.5
if is_morning and cci < -cci_level and self.Position <= 0:
self.BuyMarket()
elif is_evening and cci > cci_level and self.Position >= 0:
self.SellMarket()
self._prev_prev_candle = self._prev_candle
self._prev_candle = candle
def CreateClone(self):
return morning_evening_star_cci_strategy()