DecEMA 策略
该策略基于 DecEMA 指标来跟随趋势方向。DecEMA 通过十次连续的指数平滑并按特定系数组合,得到低滞后的移动平均线。策略比较最近三个 DecEMA 值:当曲线转向上并且当前值高于前一个值时买入并关闭空头;当曲线转向下并且当前值低于前一个值时卖出并关闭多头。
详情
- 入场条件:
- 多头:DecEMA 斜率向上,当前值 > 前值
- 空头:DecEMA 斜率向下,当前值 < 前值
- 做多/做空: 都支持
- 出场条件:
- 多头:斜率转为向下
- 空头:斜率转为向上
- 止损: 无
- 默认参数:
EmaPeriod= 3Length= 15BuyPosOpen= trueSellPosOpen= trueBuyPosClose= trueSellPosClose= trueCandleType= TimeSpan.FromHours(8).TimeFrame()
- 筛选:
- 类别: 趋势跟随
- 方向: 双向
- 指标: DecEMA
- 止损: 否
- 复杂度: 中等
- 时间框架: 中期
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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 DecEMA indicator slope.
/// Buys when the indicator turns upward and sells when it turns downward.
/// </summary>
public class DecEmaStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private decimal _prev;
private decimal _prevPrev;
private int _count;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int Length { get => _length.Value; set => _length.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DecEmaStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Base EMA Period", "Length for initial EMA", "Parameters");
_length = Param(nameof(Length), 15)
.SetGreaterThanZero()
.SetDisplay("Smoothing Length", "Smoothing length for DecEMA", "Parameters");
_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();
_prev = default;
_prevPrev = default;
_count = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_count = 0;
var decema = new DecemaIndicator
{
EmaPeriod = EmaPeriod,
Length = Length
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(decema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, decema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal decema)
{
if (candle.State != CandleStates.Finished)
return;
_count++;
if (_count <= 2)
{
_prevPrev = _prev;
_prev = decema;
return;
}
// Slope reversal detection
if (_prev < _prevPrev && decema > _prev && Position <= 0)
{
// Was falling, now turning up -> buy
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prev > _prevPrev && decema < _prev && Position >= 0)
{
// Was rising, now turning down -> sell
if (Position > 0) SellMarket();
SellMarket();
}
_prevPrev = _prev;
_prev = decema;
}
private class DecemaIndicator : DecimalLengthIndicator
{
public int EmaPeriod { get; set; } = 3;
private readonly ExponentialMovingAverage _baseEma = new();
private decimal _ema1, _ema2, _ema3, _ema4, _ema5;
private decimal _ema6, _ema7, _ema8, _ema9, _ema10;
public override void Reset()
{
base.Reset();
_baseEma.Length = EmaPeriod;
_baseEma.Reset();
_ema1 = _ema2 = _ema3 = _ema4 = _ema5 = 0m;
_ema6 = _ema7 = _ema8 = _ema9 = _ema10 = 0m;
}
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var ema0 = _baseEma.Process(input).ToDecimal();
var alpha = 2m / (1m + Length);
_ema1 = alpha * ema0 + (1 - alpha) * _ema1;
_ema2 = alpha * _ema1 + (1 - alpha) * _ema2;
_ema3 = alpha * _ema2 + (1 - alpha) * _ema3;
_ema4 = alpha * _ema3 + (1 - alpha) * _ema4;
_ema5 = alpha * _ema4 + (1 - alpha) * _ema5;
_ema6 = alpha * _ema5 + (1 - alpha) * _ema6;
_ema7 = alpha * _ema6 + (1 - alpha) * _ema7;
_ema8 = alpha * _ema7 + (1 - alpha) * _ema8;
_ema9 = alpha * _ema8 + (1 - alpha) * _ema9;
_ema10 = alpha * _ema9 + (1 - alpha) * _ema10;
var value = 10m * _ema1 - 45m * _ema2 + 120m * _ema3 - 210m * _ema4 + 252m * _ema5
- 210m * _ema6 + 120m * _ema7 - 45m * _ema8 + 10m * _ema9 - _ema10;
IsFormed = _baseEma.IsFormed;
return new DecimalIndicatorValue(this, value, input.Time);
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class dec_ema_strategy(Strategy):
def __init__(self):
super(dec_ema_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 3) \
.SetDisplay("Base EMA Period", "Length for initial EMA", "Parameters")
self._length = self.Param("Length", 15) \
.SetDisplay("Smoothing Length", "Smoothing length for DecEMA", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev = 0.0
self._prev_prev = 0.0
self._count = 0
self._base_ema = None
self._ema1 = 0.0
self._ema2 = 0.0
self._ema3 = 0.0
self._ema4 = 0.0
self._ema5 = 0.0
self._ema6 = 0.0
self._ema7 = 0.0
self._ema8 = 0.0
self._ema9 = 0.0
self._ema10 = 0.0
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def Length(self):
return self._length.Value
@Length.setter
def Length(self, value):
self._length.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(dec_ema_strategy, self).OnStarted2(time)
self._count = 0
self._base_ema = ExponentialMovingAverage()
self._base_ema.Length = self.EmaPeriod
self._ema1 = 0.0
self._ema2 = 0.0
self._ema3 = 0.0
self._ema4 = 0.0
self._ema5 = 0.0
self._ema6 = 0.0
self._ema7 = 0.0
self._ema8 = 0.0
self._ema9 = 0.0
self._ema10 = 0.0
self.SubscribeCandles(self.CandleType) \
.Bind(self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
t = candle.OpenTime
ema_result = process_float(self._base_ema, close, t, True)
if not self._base_ema.IsFormed:
return
ema0 = float(ema_result)
alpha = 2.0 / (1.0 + float(self.Length))
self._ema1 = alpha * ema0 + (1.0 - alpha) * self._ema1
self._ema2 = alpha * self._ema1 + (1.0 - alpha) * self._ema2
self._ema3 = alpha * self._ema2 + (1.0 - alpha) * self._ema3
self._ema4 = alpha * self._ema3 + (1.0 - alpha) * self._ema4
self._ema5 = alpha * self._ema4 + (1.0 - alpha) * self._ema5
self._ema6 = alpha * self._ema5 + (1.0 - alpha) * self._ema6
self._ema7 = alpha * self._ema6 + (1.0 - alpha) * self._ema7
self._ema8 = alpha * self._ema7 + (1.0 - alpha) * self._ema8
self._ema9 = alpha * self._ema8 + (1.0 - alpha) * self._ema9
self._ema10 = alpha * self._ema9 + (1.0 - alpha) * self._ema10
decema = (10.0 * self._ema1 - 45.0 * self._ema2 + 120.0 * self._ema3
- 210.0 * self._ema4 + 252.0 * self._ema5 - 210.0 * self._ema6
+ 120.0 * self._ema7 - 45.0 * self._ema8 + 10.0 * self._ema9
- self._ema10)
self._count += 1
if self._count <= 2:
self._prev_prev = self._prev
self._prev = decema
return
if self._prev < self._prev_prev and decema > self._prev and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev > self._prev_prev and decema < self._prev and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev = self._prev
self._prev = decema
def OnReseted(self):
super(dec_ema_strategy, self).OnReseted()
self._prev = 0.0
self._prev_prev = 0.0
self._count = 0
self._base_ema = None
self._ema1 = 0.0
self._ema2 = 0.0
self._ema3 = 0.0
self._ema4 = 0.0
self._ema5 = 0.0
self._ema6 = 0.0
self._ema7 = 0.0
self._ema8 = 0.0
self._ema9 = 0.0
self._ema10 = 0.0
def CreateClone(self):
return dec_ema_strategy()