Color XMACD Candle 策略
本策略在 StockSharp 中实现了 “ColorXMACDCandle” 专家顾问。它使用 MACD 指标,根据柱状图或信号线颜色的变化来产生交易信号。
思路
策略分析 MACD 某个组成部分的斜率:
- Histogram 模式 – 新柱比上一柱更高表示多头动能增强;下降表示空头动能增强。
- SignalLine 模式 – 使用 MACD 信号线的斜率。向上斜率是买入信号,向下斜率是卖出信号。
当所选组件转向上且之前不是上升时,策略可以关闭空头仓位并开多。当组件转向下且之前不是下降时,策略可以关闭多头仓位并开空。每个动作都可以通过参数单独启用或禁用。
参数
Mode– 信号来源:Histogram或SignalLine。FastPeriod– MACD 快速 EMA 周期。SlowPeriod– MACD 慢速 EMA 周期。SignalPeriod– MACD 信号线平滑周期。EnableBuyOpen– 允许开多。EnableSellOpen– 允许开空。EnableBuyClose– 允许平多。EnableSellClose– 允许平空。CandleType– 计算使用的K线类型。
交易规则
- 订阅选定的K线并计算 MACD。
- 根据模式跟踪柱状图或信号线的斜率。
- 当斜率转向上时,若允许则平空,并可开多。
- 当斜率转向下时,若允许则平多,并可开空。
策略未实现止损和止盈功能,如需风险管理可在外部添加。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that reacts to slope changes of the MACD histogram.
/// Buys when histogram slope turns up, sells when it turns down.
/// </summary>
public class ColorXMacdCandleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _signalMa;
private decimal? _prevHist;
private decimal? _prevPrevHist;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorXMacdCandleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "MACD");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "MACD");
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Signal line period", "MACD");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type for calculations", "Common");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_signalMa = null;
_prevHist = null;
_prevPrevHist = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergence();
macd.ShortMa.Length = FastPeriod;
macd.LongMa.Length = SlowPeriod;
_signalMa = new ExponentialMovingAverage { Length = SignalPeriod };
Indicators.Add(_signalMa);
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFormed)
return;
var macdLine = macdValue.GetValue<decimal>();
var signalResult = _signalMa.Process(macdValue);
if (!signalResult.IsFormed)
return;
var signalLine = signalResult.GetValue<decimal>();
var hist = macdLine - signalLine;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPrevHist = _prevHist;
_prevHist = hist;
return;
}
if (_prevHist is decimal ph && _prevPrevHist is decimal pph)
{
var wasRising = ph > pph;
var nowRising = hist > ph;
// Histogram slope turns up -> buy
if (!wasRising && nowRising && Position <= 0)
BuyMarket();
// Histogram slope turns down -> sell
else if (wasRising && !nowRising && Position >= 0)
SellMarket();
}
_prevPrevHist = _prevHist;
_prevHist = hist;
}
}
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 MovingAverageConvergenceDivergence, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_xmacd_candle_strategy(Strategy):
def __init__(self):
super(color_xmacd_candle_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA period", "MACD")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA period", "MACD")
self._signal_period = self.Param("SignalPeriod", 9) \
.SetDisplay("Signal Period", "Signal line period", "MACD")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type for calculations", "Common")
self._signal_ma = None
self._prev_hist = None
self._prev_prev_hist = None
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def signal_period(self):
return self._signal_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_xmacd_candle_strategy, self).OnReseted()
self._signal_ma = None
self._prev_hist = None
self._prev_prev_hist = None
def OnStarted2(self, time):
super(color_xmacd_candle_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergence()
macd.ShortMa.Length = self.fast_period
macd.LongMa.Length = self.slow_period
self._signal_ma = ExponentialMovingAverage()
self._signal_ma.Length = self.signal_period
self.Indicators.Add(self._signal_ma)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd)
self.DrawOwnTrades(area)
def process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFormed:
return
macd_line = float(macd_value)
signal_result = self._signal_ma.Process(macd_value)
if not signal_result.IsFormed:
return
signal_line = float(signal_result)
hist = macd_line - signal_line
if self._prev_hist is not None and self._prev_prev_hist is not None:
was_rising = self._prev_hist > self._prev_prev_hist
now_rising = hist > self._prev_hist
if not was_rising and now_rising and self.Position <= 0:
self.BuyMarket()
elif was_rising and not now_rising and self.Position >= 0:
self.SellMarket()
self._prev_prev_hist = self._prev_hist
self._prev_hist = hist
def CreateClone(self):
return color_xmacd_candle_strategy()