Color Schaff DeMarker Trend Cycle 策略
Color Schaff DeMarker Trend Cycle 策略 使用由快慢 DeMarker 值构建的自定义振荡器。该指标通过两次随机过程生成 -100 到 +100 之间的周期性数值。根据振荡器的水平和斜率分配颜色,并据此生成交易信号。
当振荡器离开上方区域时,策略开多并平空;当振荡器离开下方区域时,策略开空并平多。其思路是对极端水平的动量变化做出反应。
细节
- 入场条件:
- 多头: 前一个颜色 > 5 且当前颜色 < 6。
- 空头: 前一个颜色 < 2 且当前颜色 > 1。
- 多/空: 双向。
- 出场条件:
- 多头: 持仓时颜色 < 2。
- 空头: 持仓时颜色 > 5。
- 止损: 无明确止损和止盈。
- 默认值:
FastDeMarker= 23SlowDeMarker= 50Cycle= 10HighLevel= 60LowLevel= -60
- 过滤器:
- 类别: 趋势跟随
- 方向: 双向
- 指标: DeMarker、Highest、Lowest
- 止损: 无
- 复杂度: 中等
- 时间框架: 4H
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the Color Schaff DeMarker Trend Cycle oscillator.
/// Uses fast/slow DeMarker difference with double stochastic smoothing.
/// </summary>
public class ColorSchaffDeMarkerTrendCycleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cycle;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevSt;
private decimal _prevStc;
private bool _st1Pass;
private bool _st2Pass;
private int _prevColor;
private readonly List<decimal> _macdBuf = new();
private readonly List<decimal> _stBuf = new();
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int Cycle { get => _cycle.Value; set => _cycle.Value = value; }
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
public decimal Factor { get => _factor.Value; set => _factor.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorSchaffDeMarkerTrendCycleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("Fast DeMarker", "Fast DeMarker period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow DeMarker", "Slow DeMarker period", "Indicator");
_cycle = Param(nameof(Cycle), 10)
.SetGreaterThanZero()
.SetDisplay("Cycle", "Cycle length", "Indicator");
_highLevel = Param(nameof(HighLevel), 60m)
.SetDisplay("High Level", "Upper threshold", "Levels");
_lowLevel = Param(nameof(LowLevel), -60m)
.SetDisplay("Low Level", "Lower threshold", "Levels");
_factor = Param(nameof(Factor), 0.5m)
.SetDisplay("Factor", "Smoothing factor", "Indicator");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSt = 0; _prevStc = 0;
_st1Pass = false; _st2Pass = false;
_prevColor = 0;
_macdBuf.Clear();
_stBuf.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new DeMarker { Length = FastPeriod };
var slow = new DeMarker { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, (candle, fastVal, slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
var macd = fastVal - slowVal;
// Track high/low of macd manually
_macdBuf.Add(macd);
if (_macdBuf.Count > Cycle) _macdBuf.RemoveAt(0);
if (_macdBuf.Count < Cycle) return;
var macdHigh = decimal.MinValue;
var macdLow = decimal.MaxValue;
foreach (var v in _macdBuf)
{
if (v > macdHigh) macdHigh = v;
if (v < macdLow) macdLow = v;
}
decimal st;
if (macdHigh == macdLow) st = _prevSt;
else st = (macd - macdLow) / (macdHigh - macdLow) * 100m;
if (_st1Pass) st = Factor * (st - _prevSt) + _prevSt;
_prevSt = st;
_st1Pass = true;
_stBuf.Add(st);
if (_stBuf.Count > Cycle) _stBuf.RemoveAt(0);
if (_stBuf.Count < Cycle) return;
var stHigh = decimal.MinValue;
var stLow = decimal.MaxValue;
foreach (var v in _stBuf)
{
if (v > stHigh) stHigh = v;
if (v < stLow) stLow = v;
}
decimal stc;
if (stHigh == stLow) stc = _prevStc;
else stc = (st - stLow) / (stHigh - stLow) * 200m - 100m;
if (_st2Pass) stc = Factor * (stc - _prevStc) + _prevStc;
var dStc = stc - _prevStc;
_prevStc = stc;
_st2Pass = true;
int color;
if (stc > 0) color = stc > HighLevel ? (dStc >= 0 ? 7 : 6) : (dStc >= 0 ? 5 : 4);
else color = stc < LowLevel ? (dStc < 0 ? 0 : 1) : (dStc < 0 ? 2 : 3);
if (_prevColor > 5 && color < 6 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevColor < 2 && color > 1 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevColor = color;
})
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import DeMarker
from StockSharp.Algo.Strategies import Strategy
class color_schaff_de_marker_trend_cycle_strategy(Strategy):
def __init__(self):
super(color_schaff_de_marker_trend_cycle_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 23) \
.SetDisplay("Fast DeMarker", "Fast DeMarker period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow DeMarker", "Slow DeMarker period", "Indicator")
self._cycle = self.Param("Cycle", 10) \
.SetDisplay("Cycle", "Cycle length", "Indicator")
self._high_level = self.Param("HighLevel", 60.0) \
.SetDisplay("High Level", "Upper threshold", "Levels")
self._low_level = self.Param("LowLevel", -60.0) \
.SetDisplay("Low Level", "Lower threshold", "Levels")
self._factor = self.Param("Factor", 0.5) \
.SetDisplay("Factor", "Smoothing factor", "Indicator")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_st = 0.0
self._prev_stc = 0.0
self._st1_pass = False
self._st2_pass = False
self._prev_color = 0
self._macd_buf = []
self._st_buf = []
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def cycle(self):
return self._cycle.Value
@property
def high_level(self):
return self._high_level.Value
@property
def low_level(self):
return self._low_level.Value
@property
def factor(self):
return self._factor.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_schaff_de_marker_trend_cycle_strategy, self).OnReseted()
self._prev_st = 0.0
self._prev_stc = 0.0
self._st1_pass = False
self._st2_pass = False
self._prev_color = 0
self._macd_buf = []
self._st_buf = []
def OnStarted2(self, time):
super(color_schaff_de_marker_trend_cycle_strategy, self).OnStarted2(time)
fast = DeMarker()
fast.Length = self.fast_period
slow = DeMarker()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.on_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
slow_val = float(slow_val)
macd = fast_val - slow_val
cycle = int(self.cycle)
fct = float(self.factor)
self._macd_buf.append(macd)
if len(self._macd_buf) > cycle:
self._macd_buf.pop(0)
if len(self._macd_buf) < cycle:
return
macd_high = max(self._macd_buf)
macd_low = min(self._macd_buf)
if macd_high == macd_low:
st = self._prev_st
else:
st = (macd - macd_low) / (macd_high - macd_low) * 100.0
if self._st1_pass:
st = fct * (st - self._prev_st) + self._prev_st
self._prev_st = st
self._st1_pass = True
self._st_buf.append(st)
if len(self._st_buf) > cycle:
self._st_buf.pop(0)
if len(self._st_buf) < cycle:
return
st_high = max(self._st_buf)
st_low = min(self._st_buf)
if st_high == st_low:
stc = self._prev_stc
else:
stc = (st - st_low) / (st_high - st_low) * 200.0 - 100.0
if self._st2_pass:
stc = fct * (stc - self._prev_stc) + self._prev_stc
d_stc = stc - self._prev_stc
self._prev_stc = stc
self._st2_pass = True
hl = float(self.high_level)
ll = float(self.low_level)
if stc > 0:
if stc > hl:
color = 7 if d_stc >= 0 else 6
else:
color = 5 if d_stc >= 0 else 4
else:
if stc < ll:
color = 0 if d_stc < 0 else 1
else:
color = 2 if d_stc < 0 else 3
if self._prev_color > 5 and color < 6 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_color < 2 and color > 1 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_color = color
def CreateClone(self):
return color_schaff_de_marker_trend_cycle_strategy()